diff options
805 files changed, 58201 insertions, 37038 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index e66bca07ab..f54f13216f 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -28,6 +28,9 @@ "include-filter": "*CropLatchingTest.*" }, { + "include-filter": "*ChildLayerTest.*" + }, + { "include-filter": "*ScreenCaptureTest.*" }, { @@ -53,6 +56,9 @@ }, { "include-filter": "*RefreshRateOverlayTest.*" + }, + { + "exclude-filter": "*ChildLayerTest#ChildrenSurviveParentDestruction" } ] }, diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl index 7e892205d5..bb3faaff79 100644 --- a/aidl/gui/android/view/Surface.aidl +++ b/aidl/gui/android/view/Surface.aidl @@ -17,4 +17,4 @@ package android.view; -parcelable Surface cpp_header "gui/view/Surface.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h"; diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 5267b0294c..2e0c95a5fe 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -181,6 +181,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/clk/clk_enable/enable chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_set_rate/enable chmod 0666 /sys/kernel/tracing/events/clk/clk_set_rate/enable + chmod 0666 /sys/kernel/debug/tracing/events/printk/console/enable + chmod 0666 /sys/kernel/tracing/events/printk/console/enable # disk chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable @@ -298,6 +300,12 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger +on late-init && property:ro.boot.fastboot.boottrace=enabled + setprop debug.atrace.tags.enableflags 802922 + setprop persist.traced.enable 0 + write /sys/kernel/debug/tracing/tracing_on 1 + write /sys/kernel/tracing/tracing_on 1 + # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created # will likely fail with EBUSY as it would be in use by traced_probes. @@ -393,3 +401,10 @@ on property:persist.debug.atrace.boottrace=1 service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories disabled oneshot + +on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled + setprop debug.atrace.tags.enableflags 0 + setprop persist.traced.enable 1 + write /sys/kernel/debug/tracing/tracing_on 0 + write /sys/kernel/tracing/tracing_on 0 + diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a62bd01a5b..860a2d82e6 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -155,7 +155,10 @@ cc_test { "dumpstate.cpp", "tests/dumpstate_test.cpp", ], - static_libs: ["libgmock"], + static_libs: [ + "libc++fs", + "libgmock", + ], test_config: "dumpstate_test.xml", data: [ ":dumpstate_test_fixture", diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index e42ee0540b..42e9e0f1a7 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -51,7 +51,7 @@ static binder::Status exception(uint32_t code, const std::string& msg, // 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) { +[[noreturn]] static void* dumpstate_thread_bugreport(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 taking a bugreport. Exiting.\n"); @@ -84,11 +84,28 @@ status_t DumpstateService::Start() { return android::OK; } +binder::Status DumpstateService::preDumpUiData(const std::string&) { + std::lock_guard<std::mutex> lock(lock_); + MYLOGI("preDumpUiData()"); + + if (ds_ != nullptr) { + MYLOGE("Error! DumpstateService is currently already being used. Returning."); + return exception(binder::Status::EX_SERVICE_SPECIFIC, + "DumpstateService is already being used"); + } + + ds_ = &(Dumpstate::GetInstance()); + ds_->PreDumpUiData(); + + return binder::Status::ok(); +} + binder::Status DumpstateService::startBugreport(int32_t calling_uid, const std::string& calling_package, android::base::unique_fd bugreport_fd, android::base::unique_fd screenshot_fd, int bugreport_mode, + int bugreport_flags, const sp<IDumpstateListener>& listener, bool is_screenshot_requested) { MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); @@ -96,12 +113,12 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, // 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."); + MYLOGE("Error! DumpstateService is currently already being used. 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"); + "DumpstateService is already being used"); } // From here on, all conditions that indicate we are done with this incoming request should @@ -123,8 +140,8 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, } std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); - options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd, - screenshot_fd, is_screenshot_requested); + options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags, + bugreport_fd, screenshot_fd, is_screenshot_requested); if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) { MYLOGE("Invalid filedescriptor"); @@ -148,10 +165,9 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, pthread_t thread; // Initialize dumpstate ds_->Initialize(); - status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info); + status_t err = pthread_create(&thread, nullptr, dumpstate_thread_bugreport, ds_info); if (err != 0) { delete ds_info; - ds_info = nullptr; MYLOGE("Could not create a thread"); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); } diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index 3ec8471c04..997999c805 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -38,13 +38,16 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst status_t dump(int fd, const Vector<String16>& args) override; + binder::Status preDumpUiData(const std::string& callingPackage) override; + binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package, android::base::unique_fd bugreport_fd, android::base::unique_fd screenshot_fd, int bugreport_mode, - const sp<IDumpstateListener>& listener, + int bugreport_flags, const sp<IDumpstateListener>& listener, bool is_screenshot_requested) override; - binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package); + binder::Status cancelBugreport(int32_t calling_uid, + const std::string& calling_package) override; private: // Dumpstate object which contains all the bugreporting logic. diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 0793f0b95f..d4323af3cf 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -49,6 +49,23 @@ interface IDumpstate { // Default mode. const int BUGREPORT_MODE_DEFAULT = 6; + // Use pre-dumped data. + const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; + + /** + * Speculatively pre-dumps UI data for a bugreport request that might come later. + * + * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short + * ring buffers that might get lost by the time the actual bugreport is requested. + * + * <p>{@code startBugreport} will then pick the pre-dumped data if: + * - {@link BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified. + * - {@code preDumpUiData} and {@code startBugreport} were called by the same UID. + * + * @param callingPackage package of the original application that requested the report. + */ + void preDumpUiData(@utf8InCpp String callingPackage); + /** * Starts a bugreport in the background. * @@ -63,13 +80,14 @@ interface IDumpstate { * @param bugreportFd the file to which the zipped bugreport should be written * @param screenshotFd the file to which screenshot should be written * @param bugreportMode the mode that specifies other run time options; must be one of above + * @param bugreportFlags flags to customize the bugreport generation * @param listener callback for updates; optional * @param isScreenshotRequested indicates screenshot is requested or not */ void startBugreport(int callingUid, @utf8InCpp String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, - int bugreportMode, IDumpstateListener listener, - boolean isScreenshotRequested); + int bugreportMode, int bugreportFlags, + IDumpstateListener listener, boolean isScreenshotRequested); /** * Cancels the bugreport currently in progress. diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 12de33f2db..69a1df27b2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -241,6 +241,7 @@ static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO"; static const std::string DUMP_HALS_TASK = "DUMP HALS"; static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; +static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES"; namespace android { namespace os { @@ -1055,7 +1056,7 @@ static void DumpNetstatsProto() { return; } RunCommandToFd(fd, "", {"dumpsys", "netstats", "--proto"}, - CommandOptions::WithTimeout(120).Build()); + CommandOptions::WithTimeout(5).Build()); bool empty = 0 == lseek(fd, 0, SEEK_END); if (!empty) { ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "netstats" + kProtoExt, @@ -1606,16 +1607,16 @@ static void DumpAppInfos(int out_fd = STDOUT_FILENO) { // 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() { +Dumpstate::RunStatus Dumpstate::dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); // Enqueue slow functions into the thread pool, if the parallel run is enabled. std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins, - dump_netstats_report; + dump_netstats_report, post_process_ui_traces; if (ds.dump_pool_) { // Pool was shutdown in DumpstateDefaultAfterCritical method in order to - // drop root user. Restarts it with two threads for the parallel run. - ds.dump_pool_->start(/* thread_counts = */2); + // drop root user. Restarts it. + ds.dump_pool_->start(/* thread_counts = */3); dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1); dump_incident_report = ds.dump_pool_->enqueueTask( @@ -1625,6 +1626,8 @@ static Dumpstate::RunStatus dumpstate() { dump_board = ds.dump_pool_->enqueueTaskWithFd( DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1); + post_process_ui_traces = ds.dump_pool_->enqueueTask( + POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds); } // Dump various things. Note that anything that takes "long" (i.e. several seconds) should @@ -1747,11 +1750,6 @@ static Dumpstate::RunStatus dumpstate() { DumpFile("BINDER STATS", binder_logs_dir + "/stats"); DumpFile("BINDER STATE", binder_logs_dir + "/state"); - /* Add window and surface trace files. */ - if (!PropertiesHelper::IsUserBuild()) { - ds.AddDir(WMTRACE_DATA_DIR, false); - } - ds.AddDir(SNAPSHOTCTL_LOG_DIR, false); if (ds.dump_pool_) { @@ -1831,6 +1829,14 @@ static Dumpstate::RunStatus dumpstate() { DumpIncidentReport); } + if (ds.dump_pool_) { + WaitForTask(std::move(post_process_ui_traces)); + } else { + RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces); + } + + MaybeAddUiTracesToZip(); + return Dumpstate::RunStatus::OK; } @@ -2805,9 +2811,11 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, + int bugreport_flags, const android::base::unique_fd& bugreport_fd_in, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { + this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA; // Duplicate the fds because the passed in fds don't outlive the binder transaction. bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0)); screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0)); @@ -2935,6 +2943,10 @@ void Dumpstate::Cancel() { } } +void Dumpstate::PreDumpUiData() { + MaybeSnapshotUiTraces(); +} + /* * Dumps relevant information to a bugreport based on the given options. * @@ -3126,9 +3138,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // The trace file is added to the zip by MaybeAddSystemTraceToZip(). MaybeSnapshotSystemTrace(); - // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later - // from WMTRACE_DATA_DIR. - MaybeSnapshotWinTrace(); + // Snapshot the UI traces now (if running). + // The trace files will be added to bugreport later. + MaybeSnapshotUiTraces(); } onUiIntensiveBugreportDumpsFinished(calling_uid); MaybeCheckUserConsent(calling_uid, calling_package); @@ -3243,15 +3255,53 @@ void Dumpstate::MaybeSnapshotSystemTrace() { // file in the later stages. } -void Dumpstate::MaybeSnapshotWinTrace() { +void Dumpstate::MaybeSnapshotUiTraces() { + if (PropertiesHelper::IsUserBuild() || options_->use_predumped_ui_data) { + return; + } + // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol. - for (const auto& service : {"window", "input_method"}) { + for (const auto& service : {"input_method", "window"}) { RunCommand( // Empty name because it's not intended to be classified as a bugreport section. // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. "", {"cmd", service, "tracing", "save-for-bugreport"}, CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); } + + static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> { + "service", "call", "SurfaceFlinger", "1042" + }; + // Empty name because it's not intended to be classified as a bugreport section. + // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. + RunCommand( + "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES, + CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build()); +} + +void Dumpstate::MaybePostProcessUiTraces() { + if (PropertiesHelper::IsUserBuild()) { + return; + } + + RunCommand( + // Empty name because it's not intended to be classified as a bugreport section. + // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. + "", { + "/system/xbin/su", "system", + "/system/bin/layertracegenerator", + "/data/misc/wmtrace/transactions_trace.winscope", + "/data/misc/wmtrace/layers_trace_from_transactions.winscope" + }, + CommandOptions::WithTimeout(120).Always().RedirectStderr().Build()); +} + +void Dumpstate::MaybeAddUiTracesToZip() { + if (PropertiesHelper::IsUserBuild()) { + return; + } + + ds.AddDir(WMTRACE_DATA_DIR, false); } void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 7ffe80eba3..9f894b5079 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -204,6 +204,12 @@ class Dumpstate { BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT }; + // The flags used to customize bugreport requests. + enum BugreportFlag { + BUGREPORT_USE_PREDUMPED_UI_DATA = + android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA + }; + static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS; static Dumpstate& GetInstance(); @@ -333,6 +339,12 @@ class Dumpstate { struct DumpOptions; + /** + * Pre-dump critical UI data, e.g. data stored in short ring buffers that might get lost + * by the time the actual bugreport is requested. + */ + void PreDumpUiData(); + /* * Main entry point for running a complete bugreport. * @@ -396,6 +408,8 @@ class Dumpstate { // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead. // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide). BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; + // Will use data collected through a previous call to PreDumpUiData(). + bool use_predumped_ui_data; // File descriptor to output zip file. Takes precedence over out_dir. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. @@ -414,7 +428,8 @@ class Dumpstate { RunStatus Initialize(int argc, char* argv[]); /* Initializes options from the requested mode. */ - void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd, + void Initialize(BugreportMode bugreport_mode, int bugreport_flags, + const android::base::unique_fd& bugreport_fd, const android::base::unique_fd& screenshot_fd, bool is_screenshot_requested); @@ -535,10 +550,13 @@ class Dumpstate { RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package); RunStatus DumpstateDefaultAfterCritical(); + RunStatus dumpstate(); void MaybeTakeEarlyScreenshot(); void MaybeSnapshotSystemTrace(); - void MaybeSnapshotWinTrace(); + void MaybeSnapshotUiTraces(); + void MaybePostProcessUiTraces(); + void MaybeAddUiTracesToZip(); void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid); diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp index ec89c0dd6e..a634f9371f 100644 --- a/cmds/dumpstate/main.cpp +++ b/cmds/dumpstate/main.cpp @@ -56,7 +56,7 @@ int main(int argc, char* argv[]) { MYLOGE("Unable to start 'dumpstate' service: %d", ret); exit(1); } - MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()"); + MYLOGI("'dumpstate' service started and will wait for a call"); // Waits forever for an incoming connection. // TODO(b/111441001): should this time out? diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 28e5ee2ca9..b091c8e297 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -497,14 +497,17 @@ TEST_F(DumpstateBinderTest, Baseline) { // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip")); unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png")); + int flags = 0; 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", std::move(bugreport_fd), std::move(screenshot_fd), - Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true); + ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd), + std::move(screenshot_fd), + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener, + true); // startBugreport is an async call. Verify binder call succeeded first, then wait till listener // gets expected callbacks. EXPECT_TRUE(status.isOk()); @@ -532,6 +535,7 @@ TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + int flags = 0; EXPECT_NE(bugreport_fd.get(), -1); EXPECT_NE(screenshot_fd.get(), -1); @@ -539,9 +543,9 @@ TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { // Call startBugreport with bad arguments. sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = - ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd), - 2000, // invalid bugreport mode - listener, false); + ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd), + std::move(screenshot_fd), 2000, // invalid bugreport mode + flags, listener, false); EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); // The service should have died, freeing itself up for a new invocation. @@ -563,6 +567,7 @@ TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { unique_fd bugreport_fd2(dup(bugreport_fd.get())); unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); unique_fd screenshot_fd2(dup(screenshot_fd.get())); + int flags = 0; EXPECT_NE(bugreport_fd.get(), -1); EXPECT_NE(bugreport_fd2.get(), -1); @@ -571,14 +576,18 @@ TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = - ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd), - Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true); + ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd), + std::move(screenshot_fd), + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1, + true); 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", std::move(bugreport_fd2), std::move(screenshot_fd2), - Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true); + status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2), + std::move(screenshot_fd2), + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, + listener2, true); EXPECT_FALSE(status.isOk()); WaitTillExecutionComplete(listener2.get()); EXPECT_EQ(listener2->getErrorCode(), diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 70b4e5c0d8..1ffcafa544 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -31,6 +31,7 @@ #include <signal.h> #include <sys/types.h> #include <unistd.h> +#include <filesystem> #include <thread> #include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> @@ -237,7 +238,7 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { } TEST_F(DumpOptionsTest, InitializeFullBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); // Other options retain default values @@ -251,7 +252,7 @@ TEST_F(DumpOptionsTest, InitializeFullBugReport) { } TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_screenshot); @@ -265,7 +266,7 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { } TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false); EXPECT_TRUE(options_.is_remote_mode); EXPECT_FALSE(options_.do_vibrate); EXPECT_FALSE(options_.do_screenshot); @@ -279,7 +280,7 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { } TEST_F(DumpOptionsTest, InitializeWearBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); EXPECT_TRUE(options_.do_progress_updates); @@ -294,7 +295,7 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { } TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false); EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.telephony_only); EXPECT_TRUE(options_.do_progress_updates); @@ -309,7 +310,7 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { } TEST_F(DumpOptionsTest, InitializeWifiBugReport) { - options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false); + options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false); EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.wifi_only); @@ -982,6 +983,19 @@ TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) { EXPECT_FALSE(ds.dump_pool_); } +TEST_F(DumpstateBaseTest, PreDumpUiData) { + // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped + static const auto kTransactionsTrace = + std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"}; + + std::system(("rm " + kTransactionsTrace.string()).c_str()); + EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace)); + + Dumpstate& ds_ = Dumpstate::GetInstance(); + ds_.PreDumpUiData(); + EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace)); +} + class ZippedBugReportStreamTest : public DumpstateBaseTest { public: void SetUp() { @@ -1045,11 +1059,6 @@ TEST_F(ZippedBugReportStreamTest, StreamLimitedOnlyReport) { VerifyEntry(handle_, bugreport_txt_name, &entry); } -class DumpstateServiceTest : public DumpstateBaseTest { - public: - DumpstateService dss; -}; - class ProgressTest : public DumpstateBaseTest { public: Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") { diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index 01f7d30a42..c163095c50 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -35,9 +35,12 @@ GLHelper::GLHelper() : GLHelper::~GLHelper() { } -bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) { +bool GLHelper::setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs, + size_t numShaders) { bool result; + mDisplayToken = displayToken; + mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (mDisplay == EGL_NO_DISPLAY) { fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError()); @@ -221,14 +224,8 @@ bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, } bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) { - const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken(); - if (dpy == nullptr) { - fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n"); - return false; - } - ui::DisplayMode mode; - status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode); + status_t err = mSurfaceComposerClient->getActiveDisplayMode(mDisplayToken, &mode); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err); return false; diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h index d09463a9b8..5194f5084f 100644 --- a/cmds/flatland/GLHelper.h +++ b/cmds/flatland/GLHelper.h @@ -44,7 +44,7 @@ public: ~GLHelper(); - bool setUp(const ShaderDesc* shaderDescs, size_t numShaders); + bool setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs, size_t numShaders); void tearDown(); @@ -87,6 +87,8 @@ private: size_t mNumShaders; GLuint mDitherTexture; + + sp<IBinder> mDisplayToken; }; } // namespace android diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index 7ceb397032..6d14d568a4 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp @@ -20,6 +20,7 @@ #include <gui/SurfaceControl.h> #include <gui/GLConsumer.h> #include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> #include <ui/Fence.h> #include <utils/Trace.h> @@ -34,9 +35,10 @@ using namespace ::android; -static uint32_t g_SleepBetweenSamplesMs = 0; -static bool g_PresentToWindow = false; -static size_t g_BenchmarkNameLen = 0; +static uint32_t g_SleepBetweenSamplesMs = 0; +static bool g_PresentToWindow = false; +static size_t g_BenchmarkNameLen = 0; +static sp<IBinder> g_DisplayToken = nullptr; struct BenchmarkDesc { // The name of the test. @@ -393,7 +395,7 @@ public: uint32_t h = mDesc.runHeights[mInstance]; mGLHelper = new GLHelper(); - result = mGLHelper->setUp(shaders, NELEMS(shaders)); + result = mGLHelper->setUp(g_DisplayToken, shaders, NELEMS(shaders)); if (!result) { return false; } @@ -718,13 +720,17 @@ static size_t maxBenchmarkNameLen() { } // Print the command usage help to stderr. -static void showHelp(const char *cmd) { - fprintf(stderr, "usage: %s [options]\n", cmd); - fprintf(stderr, "options include:\n" - " -s N sleep for N ms between samples\n" - " -d display the test frame to a window\n" - " --help print this helpful message and exit\n" - ); +static void showHelp(const char* cmd) { + fprintf(stderr, "usage: %s [options]\n", cmd); + fprintf( + stderr, + "options include:\n" + " -s N sleep for N ms between samples\n" + " -d display the test frame to a window\n" + " -i display-id specify a display ID to use for multi-display device\n" + " see \"dumpsys SurfaceFlinger --display-id\" for valid " + "display IDs\n" + " --help print this helpful message and exit\n"); } int main(int argc, char** argv) { @@ -733,6 +739,14 @@ int main(int argc, char** argv) { exit(0); } + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + if (ids.empty()) { + fprintf(stderr, "Failed to get ID for any displays.\n"); + exit(3); + } + + std::optional<PhysicalDisplayId> displayId; + for (;;) { int ret; int option_index = 0; @@ -741,7 +755,7 @@ int main(int argc, char** argv) { { 0, 0, 0, 0 } }; - ret = getopt_long(argc, argv, "ds:", + ret = getopt_long(argc, argv, "ds:i:", long_options, &option_index); if (ret < 0) { @@ -757,6 +771,14 @@ int main(int argc, char** argv) { g_SleepBetweenSamplesMs = atoi(optarg); break; + case 'i': + displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg)); + if (!displayId) { + fprintf(stderr, "Invalid display ID: %s.\n", optarg); + exit(4); + } + break; + case 0: if (strcmp(long_options[option_index].name, "help")) { showHelp(argv[0]); @@ -770,6 +792,22 @@ int main(int argc, char** argv) { } } + if (!displayId) { // no display id is specified + if (ids.size() == 1) { + displayId = ids.front(); + } else { + fprintf(stderr, "Please specify a display ID for multi-display device.\n"); + showHelp(argv[0]); + exit(5); + } + } + + g_DisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + if (g_DisplayToken == nullptr) { + fprintf(stderr, "SurfaceComposer::getPhysicalDisplayToken failed.\n"); + exit(6); + } + g_BenchmarkNameLen = maxBenchmarkNameLen(); printf(" cmdline:"); @@ -782,4 +820,6 @@ int main(int argc, char** argv) { fprintf(stderr, "exiting due to error.\n"); return 1; } + + return 0; } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b1283eb4a7..bb6639e1a8 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -501,10 +501,6 @@ static int prepare_app_cache_dir(const std::string& parent, const char* name, mo } static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { - if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { - return true; - } - int32_t uid = multiuser_get_uid(userId, appId); int shared_app_gid = multiuser_get_shared_gid(userId, appId); if (shared_app_gid == -1) { diff --git a/cmds/surfacereplayer/Android.bp b/cmds/surfacereplayer/Android.bp deleted file mode 100644 index 34fc8b10ea..0000000000 --- a/cmds/surfacereplayer/Android.bp +++ /dev/null @@ -1,13 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -subdirs = [ - "proto", - "replayer", -] diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS deleted file mode 100644 index 32bcc83468..0000000000 --- a/cmds/surfacereplayer/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include platform/frameworks/native:/services/surfaceflinger/OWNERS diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp deleted file mode 100644 index 23b54ee5b0..0000000000 --- a/cmds/surfacereplayer/proto/Android.bp +++ /dev/null @@ -1,23 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -cc_library_static { - name: "libtrace_proto", - srcs: [ - "src/trace.proto", - ], - cflags: [ - "-Wall", - "-Werror", - ], - proto: { - type: "lite", - export_proto_headers: true, - }, -} diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto deleted file mode 100644 index a177027e5c..0000000000 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ /dev/null @@ -1,225 +0,0 @@ -syntax = "proto2"; -option optimize_for = LITE_RUNTIME; -package android.surfaceflinger; - -message Trace { - repeated Increment increment = 1; -} - -message Increment { - required int64 time_stamp = 1; - - oneof increment { - Transaction transaction = 2; - SurfaceCreation surface_creation = 3; - SurfaceDeletion surface_deletion = 4; - BufferUpdate buffer_update = 5; - VSyncEvent vsync_event = 6; - DisplayCreation display_creation = 7; - DisplayDeletion display_deletion = 8; - PowerModeUpdate power_mode_update = 9; - } -} - -message Transaction { - repeated SurfaceChange surface_change = 1; - repeated DisplayChange display_change = 2; - - required bool synchronous = 3; - required bool animation = 4; - optional Origin origin = 5; - optional uint64 id = 6; -} - -message SurfaceChange { - required int32 id = 1; - reserved 7; - oneof SurfaceChange { - PositionChange position = 2; - SizeChange size = 3; - AlphaChange alpha = 4; - LayerChange layer = 5; - CropChange crop = 6; - MatrixChange matrix = 8; - TransparentRegionHintChange transparent_region_hint = 10; - LayerStackChange layer_stack = 11; - HiddenFlagChange hidden_flag = 12; - OpaqueFlagChange opaque_flag = 13; - SecureFlagChange secure_flag = 14; - CornerRadiusChange corner_radius = 16; - ReparentChange reparent = 17; - RelativeParentChange relative_parent = 18; - BackgroundBlurRadiusChange background_blur_radius = 20; - ShadowRadiusChange shadow_radius = 21; - BlurRegionsChange blur_regions = 22; - TrustedOverlayChange trusted_overlay = 23; - } -} - -message PositionChange { - required float x = 1; - required float y = 2; -} - -message SizeChange { - required uint32 w = 1; - required uint32 h = 2; -} - -message AlphaChange { - required float alpha = 1; -} - -message CornerRadiusChange { - required float corner_radius = 1; -} - -message BackgroundBlurRadiusChange { - required float background_blur_radius = 1; -} - -message LayerChange { - required uint32 layer = 1; -} - -message CropChange { - required Rectangle rectangle = 1; -} - -message MatrixChange { - required float dsdx = 1; - required float dtdx = 2; - required float dsdy = 3; - required float dtdy = 4; -} - -message TransparentRegionHintChange { - repeated Rectangle region = 1; -} - -message LayerStackChange { - required uint32 layer_stack = 1; -} - -message DisplayFlagsChange { - required uint32 flags = 1; -} - -message HiddenFlagChange { - required bool hidden_flag = 1; -} - -message OpaqueFlagChange { - required bool opaque_flag = 1; -} - -message SecureFlagChange { - required bool secure_flag = 1; -} - -message DisplayChange { - required int32 id = 1; - - oneof DisplayChange { - DispSurfaceChange surface = 2; - LayerStackChange layer_stack = 3; - SizeChange size = 4; - ProjectionChange projection = 5; - DisplayFlagsChange flags = 6; - } -} - -message DispSurfaceChange { - required uint64 buffer_queue_id = 1; - required string buffer_queue_name = 2; -} - -message ProjectionChange { - required int32 orientation = 1; - required Rectangle viewport = 2; - required Rectangle frame = 3; -} - -message Rectangle { - required int32 left = 1; - required int32 top = 2; - required int32 right = 3; - required int32 bottom = 4; -} - -message SurfaceCreation { - required int32 id = 1; - required string name = 2; - required uint32 w = 3; - required uint32 h = 4; -} - -message SurfaceDeletion { - required int32 id = 1; -} - -message BufferUpdate { - required int32 id = 1; - required uint32 w = 2; - required uint32 h = 3; - required uint64 frame_number = 4; -} - -message VSyncEvent { - required int64 when = 1; -} - -message DisplayCreation { - required int32 id = 1; - required string name = 2; - optional uint64 display_id = 3; - required bool is_secure = 4; -} - -message DisplayDeletion { - required int32 id = 1; -} - -message PowerModeUpdate { - required int32 id = 1; - required int32 mode = 2; -} - -message ReparentChange { - required int32 parent_id = 1; -} - -message RelativeParentChange { - required int32 relative_parent_id = 1; - required int32 z = 2; -} - -message ShadowRadiusChange { - required float radius = 1; -} - -message TrustedOverlayChange { - required float is_trusted_overlay = 1; -} - -message BlurRegionsChange { - repeated BlurRegionChange blur_regions = 1; -} - -message BlurRegionChange { - required uint32 blur_radius = 1; - required float corner_radius_tl = 2; - required float corner_radius_tr = 3; - required float corner_radius_bl = 4; - required float corner_radius_br = 5; - required float alpha = 6; - required int32 left = 7; - required int32 top = 8; - required int32 right = 9; - required int32 bottom = 10; -} - -message Origin { - required int32 pid = 1; - required int32 uid = 2; -} diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp deleted file mode 100644 index 3985230f08..0000000000 --- a/cmds/surfacereplayer/replayer/Android.bp +++ /dev/null @@ -1,71 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -cc_library_shared { - name: "libsurfacereplayer", - srcs: [ - "BufferQueueScheduler.cpp", - "Event.cpp", - "Replayer.cpp", - ], - cppflags: [ - "-Werror", - "-Wno-unused-parameter", - "-Wno-format", - "-Wno-c++98-compat-pedantic", - "-Wno-float-conversion", - "-Wno-disabled-macro-expansion", - "-Wno-float-equal", - "-Wno-sign-conversion", - "-Wno-padded", - ], - static_libs: [ - "libtrace_proto", - ], - shared_libs: [ - "libEGL", - "libGLESv2", - "libbinder", - "liblog", - "libcutils", - "libgui", - "libui", - "libutils", - "libprotobuf-cpp-lite", - "libbase", - "libnativewindow", - ], - export_include_dirs: [ - ".", - ], -} - -cc_binary { - name: "surfacereplayer", - srcs: [ - "Main.cpp", - ], - shared_libs: [ - "libprotobuf-cpp-lite", - "libsurfacereplayer", - "libutils", - "libgui", - ], - static_libs: [ - "libtrace_proto", - ], - cppflags: [ - "-Werror", - "-Wno-unused-parameter", - "-Wno-c++98-compat-pedantic", - "-Wno-float-conversion", - "-Wno-disabled-macro-expansion", - "-Wno-float-equal", - ], -} diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp deleted file mode 100644 index 77de8dc44c..0000000000 --- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define LOG_TAG "BufferQueueScheduler" - -#include "BufferQueueScheduler.h" - -#include <android/native_window.h> -#include <gui/Surface.h> - -using namespace android; - -BufferQueueScheduler::BufferQueueScheduler( - const sp<SurfaceControl>& surfaceControl, const HSV& color, int id) - : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {} - -void BufferQueueScheduler::startScheduling() { - ALOGV("Starting Scheduler for %d Layer", mSurfaceId); - std::unique_lock<std::mutex> lock(mMutex); - if (mSurfaceControl == nullptr) { - mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); }); - } - - while (mContinueScheduling) { - while (true) { - if (mBufferEvents.empty()) { - break; - } - - BufferEvent event = mBufferEvents.front(); - lock.unlock(); - - bufferUpdate(event.dimensions); - fillSurface(event.event); - mColor.modulate(); - lock.lock(); - mBufferEvents.pop(); - } - mCondition.wait(lock); - } -} - -void BufferQueueScheduler::addEvent(const BufferEvent& event) { - std::lock_guard<std::mutex> lock(mMutex); - mBufferEvents.push(event); - mCondition.notify_one(); -} - -void BufferQueueScheduler::stopScheduling() { - std::lock_guard<std::mutex> lock(mMutex); - mContinueScheduling = false; - mCondition.notify_one(); -} - -void BufferQueueScheduler::setSurfaceControl( - const sp<SurfaceControl>& surfaceControl, const HSV& color) { - std::lock_guard<std::mutex> lock(mMutex); - mSurfaceControl = surfaceControl; - mColor = color; - mCondition.notify_one(); -} - -void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) { - sp<Surface> s = mSurfaceControl->getSurface(); - s->setBuffersDimensions(dimensions.width, dimensions.height); -} - -void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) { - ANativeWindow_Buffer outBuffer; - sp<Surface> s = mSurfaceControl->getSurface(); - - status_t status = s->lock(&outBuffer, nullptr); - - if (status != NO_ERROR) { - ALOGE("fillSurface: failed to lock buffer, (%d)", status); - return; - } - - auto color = mColor.getRGB(); - - auto img = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; y++) { - for (int x = 0; x < outBuffer.width; x++) { - uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); - pixel[0] = color.r; - pixel[1] = color.g; - pixel[2] = color.b; - pixel[3] = LAYER_ALPHA; - } - } - - event->readyToExecute(); - - status = s->unlockAndPost(); - - ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status); -} diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h deleted file mode 100644 index cb20fcc798..0000000000 --- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 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_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H -#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H - -#include "Color.h" -#include "Event.h" - -#include <gui/SurfaceControl.h> - -#include <utils/StrongPointer.h> - -#include <atomic> -#include <condition_variable> -#include <mutex> -#include <queue> -#include <utility> - -namespace android { - -auto constexpr LAYER_ALPHA = 190; - -struct Dimensions { - Dimensions() = default; - Dimensions(int w, int h) : width(w), height(h) {} - - int width = 0; - int height = 0; -}; - -struct BufferEvent { - BufferEvent() = default; - BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {} - - std::shared_ptr<Event> event; - Dimensions dimensions; -}; - -class BufferQueueScheduler { - public: - BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id); - - void startScheduling(); - void addEvent(const BufferEvent&); - void stopScheduling(); - - void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color); - - private: - void bufferUpdate(const Dimensions& dimensions); - - // Lock and fill the surface, block until the event is signaled by the main loop, - // then unlock and post the buffer. - void fillSurface(const std::shared_ptr<Event>& event); - - sp<SurfaceControl> mSurfaceControl; - HSV mColor; - const int mSurfaceId; - - bool mContinueScheduling; - - std::queue<BufferEvent> mBufferEvents; - std::mutex mMutex; - std::condition_variable mCondition; -}; - -} // namespace android -#endif diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h deleted file mode 100644 index ce644be7be..0000000000 --- a/cmds/surfacereplayer/replayer/Color.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2016 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_SURFACEREPLAYER_COLOR_H -#define ANDROID_SURFACEREPLAYER_COLOR_H - -#include <cmath> -#include <cstdlib> - -namespace android { - -constexpr double modulateFactor = .0001; -constexpr double modulateLimit = .80; - -struct RGB { - RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {} - - uint8_t r = 0; - uint8_t g = 0; - uint8_t b = 0; -}; - -struct HSV { - HSV() = default; - HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {} - - double h = 0; - double s = 0; - double v = 0; - - RGB getRGB() const; - - bool modulateUp = false; - - void modulate(); -}; - -void inline HSV::modulate() { - if(modulateUp) { - v += modulateFactor; - } else { - v -= modulateFactor; - } - - if(v <= modulateLimit || v >= 1) { - modulateUp = !modulateUp; - } -} - -inline RGB HSV::getRGB() const { - using namespace std; - double r = 0, g = 0, b = 0; - - if (s == 0) { - r = v; - g = v; - b = v; - } else { - auto tempHue = static_cast<int>(h) % 360; - tempHue = tempHue / 60; - - int i = static_cast<int>(trunc(tempHue)); - double f = h - i; - - double x = v * (1.0 - s); - double y = v * (1.0 - (s * f)); - double z = v * (1.0 - (s * (1.0 - f))); - - switch (i) { - case 0: - r = v; - g = z; - b = x; - break; - - case 1: - r = y; - g = v; - b = x; - break; - - case 2: - r = x; - g = v; - b = z; - break; - - case 3: - r = x; - g = y; - b = v; - break; - - case 4: - r = z; - g = x; - b = v; - break; - - default: - r = v; - g = x; - b = y; - break; - } - } - - return RGB(round(r * 255), round(g * 255), round(b * 255)); -} -} -#endif diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp deleted file mode 100644 index 64db5f07b1..0000000000 --- a/cmds/surfacereplayer/replayer/Event.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016 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 "Event.h" - -using namespace android; -using Increment = surfaceflinger::Increment; - -Event::Event(Increment::IncrementCase type) : mIncrementType(type) {} - -void Event::readyToExecute() { - changeState(Event::EventState::Waiting); - waitUntil(Event::EventState::Signaled); - changeState(Event::EventState::Running); -} - -void Event::complete() { - waitUntil(Event::EventState::Waiting); - changeState(Event::EventState::Signaled); - waitUntil(Event::EventState::Running); -} - -void Event::waitUntil(Event::EventState state) { - std::unique_lock<std::mutex> lock(mLock); - mCond.wait(lock, [this, state] { return (mState == state); }); -} - -void Event::changeState(Event::EventState state) { - std::unique_lock<std::mutex> lock(mLock); - mState = state; - lock.unlock(); - - mCond.notify_one(); -} - -Increment::IncrementCase Event::getIncrementType() { - return mIncrementType; -} diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h deleted file mode 100644 index 09a7c248d5..0000000000 --- a/cmds/surfacereplayer/replayer/Event.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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_SURFACEREPLAYER_EVENT_H -#define ANDROID_SURFACEREPLAYER_EVENT_H - -#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h> - -#include <condition_variable> -#include <mutex> - -namespace android { - -using Increment = surfaceflinger::Increment; - -class Event { - public: - Event(Increment::IncrementCase); - - enum class EventState { - SettingUp, // Completing as much time-independent work as possible - Waiting, // Waiting for signal from main thread to finish execution - Signaled, // Signaled by main thread, about to immediately switch to Running - Running // Finishing execution of rest of work - }; - - void readyToExecute(); - void complete(); - - Increment::IncrementCase getIncrementType(); - - private: - void waitUntil(EventState state); - void changeState(EventState state); - - std::mutex mLock; - std::condition_variable mCond; - - EventState mState = EventState::SettingUp; - - Increment::IncrementCase mIncrementType; -}; -} -#endif diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp deleted file mode 100644 index fbfcacf1aa..0000000000 --- a/cmds/surfacereplayer/replayer/Main.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2016 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. - */ - -/* - * Replayer - Main.cpp - * - * 1. Get flags from command line - * 2. Commit actions or settings based on the flags - * 3. Initalize a replayer object with the filename passed in - * 4. Replay - * 5. Exit successfully or print error statement - */ - -#include <Replayer.h> - -#include <csignal> -#include <iostream> -#include <stdlib.h> -#include <unistd.h> - -using namespace android; - -void printHelpMenu() { - std::cout << "SurfaceReplayer options:\n"; - std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n"; - std::cout << " File path must be absolute" << std::endl << std::endl; - - std::cout << " -m Stops the replayer at the start of the trace and switches "; - "to manual replay\n"; - - std::cout << "\n -t [Number of Threads] Specifies the number of threads to be used while " - "replaying (default is " << android::DEFAULT_THREADS << ")\n"; - - std::cout << "\n -s [Timestamp] Specify at what timestamp should the replayer switch " - "to manual replay\n"; - - std::cout << " -n Ignore timestamps and run through trace as fast as possible\n"; - - std::cout << " -l Indefinitely loop the replayer\n"; - - std::cout << " -h Display help menu\n"; - - std::cout << std::endl; -} - -int main(int argc, char** argv) { - std::string filename; - bool loop = false; - bool wait = true; - bool pauseBeginning = false; - int numThreads = DEFAULT_THREADS; - long stopHere = -1; - - int opt = 0; - while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) { - switch (opt) { - case 'm': - pauseBeginning = true; - break; - case 't': - numThreads = atoi(optarg); - break; - case 's': - stopHere = atol(optarg); - break; - case 'n': - wait = false; - break; - case 'l': - loop = true; - break; - case 'h': - case '?': - printHelpMenu(); - exit(0); - default: - std::cerr << "Invalid argument...exiting" << std::endl; - printHelpMenu(); - exit(0); - } - } - - char** input = argv + optind; - if (input[0] == nullptr) { - std::cerr << "No trace file provided...exiting" << std::endl; - abort(); - } - filename.assign(input[0]); - - status_t status = NO_ERROR; - do { - android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere); - status = r.replay(); - } while(loop); - - if (status == NO_ERROR) { - std::cout << "Successfully finished replaying trace" << std::endl; - } else { - std::cerr << "Trace replayer returned error: " << status << std::endl; - } - - return 0; -} diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md deleted file mode 100644 index 893f0dc0f6..0000000000 --- a/cmds/surfacereplayer/replayer/README.md +++ /dev/null @@ -1,262 +0,0 @@ -SurfaceReplayer Documentation -=================== - -[go/SurfaceReplayer](go/SurfaceReplayer) - -SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by -[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays - -* Creation and deletion of surfaces/displays -* Alterations to the surfaces/displays called Transactions -* Buffer Updates to surfaces -* VSync events - -At their specified times to be as close to the original trace. - -Usage --------- - -###Creating a trace - -SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to -utilize it. To allow it to write to the device, run - -`setenforce 0` - -To start recording a trace, run - -`service call SurfaceFlinger 1020 i32 1` - -To stop recording, run - -`service call SurfaceFlinger 1020 i32 0` - -The default location for the trace is `/data/SurfaceTrace.dat` - -###Executable - -To replay a specific trace, execute - -`/data/local/tmp/surfacereplayer /absolute/path/to/trace` - -inside the android shell. This will replay the full trace and then exit. Running this command -outside of the shell by prepending `adb shell` will not allow for manual control and will not turn -off VSync injections if it interrupted in any way other than fully replaying the trace - -The replay will not fill surfaces with their contents during the capture. Rather they are given a -random color which will be the same every time the trace is replayed. Surfaces modulate their color -at buffer updates. - -**Options:** - -- -m pause the replayer at the start of the trace for manual replay -- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3) -- -s [Timestamp] switches to manual replay at specified timestamp -- -n Ignore timestamps and run through trace as fast as possible -- -l Indefinitely loop the replayer -- -h displays help menu - -**Manual Replay:** -When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled -by the user. Pressing CTRL-C again will exit the replayer. - -Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to -input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter -without inputting a command repeats the previous command. - -- n - steps the replayer to the next VSync event -- ni - steps the replayer to the next increment -- c - continues normal replaying -- c [milliseconds] - continue until specified number of milliseconds have passed -- s [timestamp] - continue and stop at specified timestamp -- l - list out timestamp of current increment -- h - displays help menu - -###Shared Library - -To use the shared library include these shared libraries - -`libsurfacereplayer` -`libprotobuf-cpp-full` -`libutils` - -And the static library - -`libtrace_proto` - -Include the replayer header at the top of your file - -`#include <replayer/Replayer.h>` - -There are two constructors for the replayer - -`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)` -`Replayer(Trace& trace, ... ditto ...)` - -The first constructor takes in the filepath where the trace is located and loads in the trace -object internally. -- replayManually - **True**: if the replayer will immediately switch to manual replay at the start -- numThreads - Number of worker threads the replayer will use. -- wait - **False**: Replayer ignores waits in between increments -- stopHere - Time stamp of where the replayer should run to then switch to manual replay - -The second constructor includes all of the same parameters but takes in a preloaded trace object. -To use add - -`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>` - -To your file - -After initializing the Replayer call - - replayer.replay(); - -And the trace will start replaying. Once the trace is finished replaying, the function will return. -The layers that are visible at the end of the trace will remain on screen until the program -terminates. - - -**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was -never executed. This can be fixed by executing - -`service call SurfaceFlinger 23 i32 0` - -in the android shell - -Code Breakdown -------------- - -The Replayer is composed of 5 components. - -- The data format of the trace (Trace.proto) -- The Replayer object (Replayer.cpp) -- The synchronization mechanism to signal threads within the Replayer (Event.cpp) -- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp) -- The Main executable (Main.cpp) - -### Traces - -Traces are represented as a protobuf message located in surfacereplayer/proto/src. - -**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger). -**Increments** contain the time stamp of when it occurred and a *oneof* which can be a - - - Transaction - - SurfaceCreation - - SurfaceDeletion - - DisplayCreation - - DisplayDeleteion - - BufferUpdate - - VSyncEvent - - PowerModeUpdate - -**Transactions** contain whether the transaction was synchronous or animated and *repeated* -**SurfaceChanges** and **DisplayChanges** - -- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as -position, alpha, hidden, size, etc. -- **DisplayChanges** contain the id of the display being manipulated and can be changes such as -size, layer stack, projection, etc. - -**Surface/Display Creation** contain the id of the surface/display and the name of the -surface/display - -**Surface/Display Deletion** contain the id of the surface/display to be deleted - -**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the -buffer, and the frame number. - -**VSyncEvents** contain when the VSync event has occurred. - -**PowerModeUpdates** contain the id of the display being updated and what mode it is being -changed to. - -To output the contents of a trace in a readable format, execute - -`**aprotoc** --decode=Trace \ --I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \ -$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \ - < **YourTraceFile.dat** > **YourOutputName.txt**` - - -###Replayer - -Fundamentally the replayer loads a trace and iterates through each increment, waiting the required -amount of time until the increment should be executed, then executing the increment. The first -increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and -goes from there. - -Increments from the trace are played asynchronously rather than one by one, being dispatched by -the main thread, queued up in a thread pool and completed when the main thread deems they are -ready to finish execution. - -When an increment is dispatched, it completes as much work as it can before it has to be -synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action -(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event -object. The main thread holds a queue of these Event objects and completes the -corresponding Event base on its time stamp. After completing an increment, the main thread will -dispatch another increment and continue. - -The main thread's execution flow is outlined below - - initReplay() //queue up the initial increments - while(!pendingIncrements.empty()) { //while increments remaining - event = pendingIncrement.pop(); - wait(event.time_stamp(); //waitUntil it is time to complete this increment - - event.complete() //signal to let event finish - if(increments remaing()) { - dispatchEvent() //queue up another increment - } - } - -A worker thread's flow looks like so - - //dispatched! - Execute non-time sensitive work here - ... - event.readyToExecute() //time sensitive point...waiting for Main Thread - ... - Finish execution - - -### Event - -An Event is a simple synchronization mechanism used to facilitate communication between the main -and worker threads. Every time an increment is dispatched, an Event object is also created. - -An Event can be in 4 different states: - -- **SettingUp** - The worker is in the process of completing all non-time sensitive work -- **Waiting** - The worker is waiting on the main thread to signal it. -- **Signaled** - The worker has just been signaled by the main thread -- **Running** - The worker is running again and finishing the rest of its work. - -When the main thread wants to finish the execution of a worker, the worker can either still be -**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the -main thread will **Signal** it to complete. The worker thread changes itself to the **Running** -state once **Signaled**. This last step exists in order to communicate back to the main thread that -the worker thread has actually started completing its execution, rather than being preempted right -after signalling. Once this happens, the main thread schedules the next worker. This makes sure -there is a constant amount of workers running at one time. - -This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the -worker and main thread respectively. - -### BufferQueueScheduler - -During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a -buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential -**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it, -which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by -handling when **BufferUpdates** should be scheduled, making sure that they don't overlap. - -When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a -**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one -every time an Event is completed. - -### Main - -The main exectuable reads in the command line arguments. Creates the Replayer using those -arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit -gracefully, if there are then it will report the error and then exit. diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 3f7c7d6a7b..44235ccdef 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -464,7 +464,6 @@ void Replayer::setPosition(SurfaceComposerClient::Transaction& t, void Replayer::setSize(SurfaceComposerClient::Transaction& t, layer_id id, const SizeChange& sc) { ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h()); - t.setSize(mLayers[id], sc.w(), sc.h()); } void Replayer::setLayer(SurfaceComposerClient::Transaction& t, diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h deleted file mode 100644 index d62522a497..0000000000 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2016 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_SURFACEREPLAYER_H -#define ANDROID_SURFACEREPLAYER_H - -#include "BufferQueueScheduler.h" -#include "Color.h" -#include "Event.h" - -#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h> - -#include <gui/SurfaceComposerClient.h> -#include <gui/SurfaceControl.h> - -#include <utils/Errors.h> -#include <utils/StrongPointer.h> - -#include <stdatomic.h> -#include <condition_variable> -#include <memory> -#include <mutex> -#include <queue> -#include <thread> -#include <unordered_map> -#include <utility> - -using namespace android::surfaceflinger; - -namespace android { - -const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat"; -const auto RAND_COLOR_SEED = 700; -const auto DEFAULT_THREADS = 3; - -typedef int32_t layer_id; -typedef int32_t display_id; - -typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges; -typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges; - -class Replayer { - public: - Replayer(const std::string& filename, bool replayManually = false, - int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1); - Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS, - bool wait = true, nsecs_t stopHere = -1); - - status_t replay(); - - private: - status_t initReplay(); - - void waitForConsoleCommmand(); - static void stopAutoReplayHandler(int signal); - - status_t dispatchEvent(int index); - - status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event); - status_t createSurfaceControl(const SurfaceCreation& create, - const std::shared_ptr<Event>& event); - status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event); - void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event); - void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event); - void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event); - - status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction, - const SurfaceChanges& surfaceChange); - void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction, - const DisplayChanges& displayChange); - - void setPosition(SurfaceComposerClient::Transaction& t, - layer_id id, const PositionChange& pc); - void setSize(SurfaceComposerClient::Transaction& t, - layer_id id, const SizeChange& sc); - void setAlpha(SurfaceComposerClient::Transaction& t, - layer_id id, const AlphaChange& ac); - void setLayer(SurfaceComposerClient::Transaction& t, - layer_id id, const LayerChange& lc); - void setCrop(SurfaceComposerClient::Transaction& t, - layer_id id, const CropChange& cc); - void setCornerRadius(SurfaceComposerClient::Transaction& t, - layer_id id, const CornerRadiusChange& cc); - void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t, - layer_id id, const BackgroundBlurRadiusChange& cc); - void setBlurRegions(SurfaceComposerClient::Transaction& t, - layer_id id, const BlurRegionsChange& cc); - void setMatrix(SurfaceComposerClient::Transaction& t, - layer_id id, const MatrixChange& mc); - void setTransparentRegionHint(SurfaceComposerClient::Transaction& t, - layer_id id, const TransparentRegionHintChange& trgc); - void setLayerStack(SurfaceComposerClient::Transaction& t, - layer_id id, const LayerStackChange& lsc); - void setHiddenFlag(SurfaceComposerClient::Transaction& t, - layer_id id, const HiddenFlagChange& hfc); - void setOpaqueFlag(SurfaceComposerClient::Transaction& t, - layer_id id, const OpaqueFlagChange& ofc); - void setSecureFlag(SurfaceComposerClient::Transaction& t, - layer_id id, const SecureFlagChange& sfc); - void setReparentChange(SurfaceComposerClient::Transaction& t, - layer_id id, const ReparentChange& c); - void setRelativeParentChange(SurfaceComposerClient::Transaction& t, - layer_id id, const RelativeParentChange& c); - void setShadowRadiusChange(SurfaceComposerClient::Transaction& t, - layer_id id, const ShadowRadiusChange& c); - void setBlurRegionsChange(SurfaceComposerClient::Transaction& t, - layer_id id, const BlurRegionsChange& c); - - void setDisplaySurface(SurfaceComposerClient::Transaction& t, - display_id id, const DispSurfaceChange& dsc); - void setDisplayLayerStack(SurfaceComposerClient::Transaction& t, - display_id id, const LayerStackChange& lsc); - void setDisplaySize(SurfaceComposerClient::Transaction& t, - display_id id, const SizeChange& sc); - void setDisplayProjection(SurfaceComposerClient::Transaction& t, - display_id id, const ProjectionChange& pc); - - void waitUntilTimestamp(int64_t timestamp); - status_t loadSurfaceComposerClient(); - - Trace mTrace; - bool mLoaded = false; - int32_t mIncrementIndex = 0; - int64_t mCurrentTime = 0; - int32_t mNumThreads = DEFAULT_THREADS; - - Increment mCurrentIncrement; - - std::string mLastInput; - - static atomic_bool sReplayingManually; - bool mWaitingForNextVSync; - bool mWaitForTimeStamps; - nsecs_t mStopTimeStamp; - bool mHasStopped; - - std::mutex mLayerLock; - std::condition_variable mLayerCond; - std::unordered_map<layer_id, sp<SurfaceControl>> mLayers; - std::unordered_map<layer_id, HSV> mColors; - - std::mutex mPendingLayersLock; - std::vector<layer_id> mLayersPendingRemoval; - - std::mutex mBufferQueueSchedulerLock; - std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers; - - std::mutex mDisplayLock; - std::condition_variable mDisplayCond; - std::unordered_map<display_id, sp<IBinder>> mDisplays; - - sp<SurfaceComposerClient> mComposerClient; - std::queue<std::shared_ptr<Event>> mPendingIncrements; -}; - -} // namespace android -#endif diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py deleted file mode 100644 index 58bfbf3c43..0000000000 --- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/python -from subprocess import call -import os -proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/" -call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"]) - -from trace_pb2 import * - -trace = Trace() - -def main(): - global trace - while(1): - option = main_menu() - - if option == 0: - break - - increment = trace.increment.add() - increment.time_stamp = int(input("Time stamp of action: ")) - - if option == 1: - transaction(increment) - elif option == 2: - surface_create(increment) - elif option == 3: - surface_delete(increment) - elif option == 4: - display_create(increment) - elif option == 5: - display_delete(increment) - elif option == 6: - buffer_update(increment) - elif option == 7: - vsync_event(increment) - elif option == 8: - power_mode_update(increment) - - seralizeTrace() - -def seralizeTrace(): - with open("trace.dat", 'wb') as f: - f.write(trace.SerializeToString()) - - -def main_menu(): - print ("") - print ("What would you like to do?") - print ("1. Add transaction") - print ("2. Add surface creation") - print ("3. Add surface deletion") - print ("4. Add display creation") - print ("5. Add display deletion") - print ("6. Add buffer update") - print ("7. Add VSync event") - print ("8. Add power mode update") - print ("0. Finish and serialize") - print ("") - - return int(input("> ")) - -def transaction_menu(): - print ("") - print ("What kind of transaction?") - print ("1. Position Change") - print ("2. Size Change") - print ("3. Alpha Change") - print ("4. Layer Change") - print ("5. Crop Change") - print ("6. Final Crop Change") - print ("7. Matrix Change") - print ("9. Transparent Region Hint Change") - print ("10. Layer Stack Change") - print ("11. Hidden Flag Change") - print ("12. Opaque Flag Change") - print ("13. Secure Flag Change") - print ("14. Deferred Transaction Change") - print ("15. Display - Surface Change") - print ("16. Display - Layer Stack Change") - print ("17. Display - Size Change") - print ("18. Display - Projection Change") - print ("0. Finished adding Changes to this transaction") - print ("") - - return int(input("> ")) - -def transaction(increment): - global trace - - increment.transaction.synchronous \ - = bool(input("Is transaction synchronous (True/False): ")) - increment.transaction.animation \ - = bool(input("Is transaction animated (True/False): ")) - - while(1): - option = transaction_menu() - - if option == 0: - break - - change = None - if option <= 14: - change = increment.transaction.surface_change.add() - elif option >= 15 and option <= 18: - change = increment.transaction.display_change.add() - - change.id = int(input("ID of layer/display to undergo a change: ")) - - if option == 1: - change.position.x, change.position.y = position() - elif option == 2: - change.size.w, change.size.h = size() - elif option == 3: - change.alpha.alpha = alpha() - elif option == 4: - change.layer.layer = layer() - elif option == 5: - change.crop.rectangle.left, change.crop.rectangle.top, \ - change.crop.rectangle.right, change.crop.rectangle.bottom = crop() - elif option == 6: - change.final_crop.rectangle.left, \ - change.final_crop.rectangle.top, \ - change.final_crop.rectangle.right,\ - change.final_crop.rectangle.bottom = final_crop() - elif option == 7: - change.matrix.dsdx,\ - change.matrix.dtdx,\ - change.matrix.dsdy,\ - change.matrix.dtdy = layer() - elif option == 9: - for rect in transparent_region_hint(): - new = increment.transparent_region_hint.region.add() - new.left = rect[0] - new.top = rect[1] - new.right = rect[2] - new.bottom = rect[3] - elif option == 10: - change.layer_stack.layer_stack = layer_stack() - elif option == 11: - change.hidden_flag.hidden_flag = hidden_flag() - elif option == 12: - change.opaque_flag.opaque_flag = opaque_flag() - elif option == 13: - change.secure_flag.secure_flag = secure_flag() - elif option == 14: - change.deferred_transaction.layer_id, \ - change.deferred_transaction.frame_number = deferred_transaction() - elif option == 15: - change.surface.buffer_queue_id, \ - change.surface.buffer_queue_name = surface() - elif option == 16: - change.layer_stack.layer_stack = layer_stack() - elif option == 17: - change.size.w, change.size.h = size() - elif option == 18: - projection(change) - -def surface_create(increment): - increment.surface_creation.id = int(input("Enter id: ")) - n = str(raw_input("Enter name: ")) - increment.surface_creation.name = n - increment.surface_creation.w = input("Enter w: ") - increment.surface_creation.h = input("Enter h: ") - -def surface_delete(increment): - increment.surface_deletion.id = int(input("Enter id: ")) - -def display_create(increment): - increment.display_creation.id = int(input("Enter id: ")) - increment.display_creation.name = str(raw_input("Enter name: ")) - increment.display_creation.display_id = int(input("Enter display ID: ")) - increment.display_creation.is_secure = bool(input("Enter if secure: ")) - -def display_delete(increment): - increment.surface_deletion.id = int(input("Enter id: ")) - -def buffer_update(increment): - increment.buffer_update.id = int(input("Enter id: ")) - increment.buffer_update.w = int(input("Enter w: ")) - increment.buffer_update.h = int(input("Enter h: ")) - increment.buffer_update.frame_number = int(input("Enter frame_number: ")) - -def vsync_event(increment): - increment.vsync_event.when = int(input("Enter when: ")) - -def power_mode_update(increment): - increment.power_mode_update.id = int(input("Enter id: ")) - increment.power_mode_update.mode = int(input("Enter mode: ")) - -def position(): - x = input("Enter x: ") - y = input("Enter y: ") - - return float(x), float(y) - -def size(): - w = input("Enter w: ") - h = input("Enter h: ") - - return int(w), int(h) - -def alpha(): - alpha = input("Enter alpha: ") - - return float(alpha) - -def layer(): - layer = input("Enter layer: ") - - return int(layer) - -def crop(): - return rectangle() - -def final_crop(): - return rectangle() - -def matrix(): - dsdx = input("Enter dsdx: ") - dtdx = input("Enter dtdx: ") - dsdy = input("Enter dsdy: ") - dtdy = input("Enter dtdy: ") - - return float(dsdx) - -def transparent_region_hint(): - num = input("Enter number of rectangles in region: ") - - return [rectangle() in range(x)] - -def layer_stack(): - layer_stack = input("Enter layer stack: ") - - return int(layer_stack) - -def hidden_flag(): - flag = input("Enter hidden flag state (True/False): ") - - return bool(flag) - -def opaque_flag(): - flag = input("Enter opaque flag state (True/False): ") - - return bool(flag) - -def secure_flag(): - flag = input("Enter secure flag state (True/False): ") - - return bool(flag) - -def deferred_transaction(): - layer_id = input("Enter layer_id: ") - frame_number = input("Enter frame_number: ") - - return int(layer_id), int(frame_number) - -def surface(): - id = input("Enter id: ") - name = raw_input("Enter name: ") - - return int(id), str(name) - -def projection(change): - change.projection.orientation = input("Enter orientation: ") - print("Enter rectangle for viewport") - change.projection.viewport.left, \ - change.projection.viewport.top, \ - change.projection.viewport.right,\ - change.projection.viewport.bottom = rectangle() - print("Enter rectangle for frame") - change.projection.frame.left, \ - change.projection.frame.top, \ - change.projection.frame.right,\ - change.projection.frame.bottom = rectangle() - -def rectangle(): - left = input("Enter left: ") - top = input("Enter top: ") - right = input("Enter right: ") - bottom = input("Enter bottom: ") - - return int(left), int(top), int(right), int(bottom) - -if __name__ == "__main__": - main() diff --git a/data/etc/android.hardware.type.automotive.xml b/data/etc/android.hardware.type.automotive.xml index 113945b0c5..a9b4b0526a 100644 --- a/data/etc/android.hardware.type.automotive.xml +++ b/data/etc/android.hardware.type.automotive.xml @@ -17,6 +17,4 @@ <!-- These features determine that the device running android is a car. --> <permissions> <feature name="android.hardware.type.automotive" /> - <!-- TODO: Revert this after enabling work profiles refer b/170332519 --> - <unavailable-feature name="android.software.managed_users"/> </permissions> diff --git a/data/etc/android.software.credentials.xml b/data/etc/android.software.credentials.xml new file mode 100644 index 0000000000..234d14411c --- /dev/null +++ b/data/etc/android.software.credentials.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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. +--> + +<permissions> + <feature name="android.software.credentials" /> +</permissions> diff --git a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml new file mode 100644 index 0000000000..d0b594c73d --- /dev/null +++ b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature indicating that the device passes OpenGL ES + dEQP tests associated with date 2023-03-01 (0x07E70301). --> +<permissions> + <feature name="android.software.opengles.deqp.level" version="132580097" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml new file mode 100644 index 0000000000..6ae248ac3c --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature indicating that the device passes Vulkan dEQP + tests associated with date 2023-03-01 (0x07E70301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132580097" /> +</permissions> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 8fdd8d06d5..08330126c7 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -52,6 +52,7 @@ <feature name="android.software.print" /> <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> + <feature name="android.software.credentials" /> <feature name="android.software.cant_save_state" /> <feature name="android.software.secure_lock_screen" /> <feature name="android.software.window_magnification" /> diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index 59d5b10947..6af4d91418 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml @@ -52,6 +52,7 @@ <feature name="android.software.print" /> <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> + <feature name="android.software.credentials" /> <feature name="android.software.cant_save_state" /> <feature name="android.software.secure_lock_screen" /> <feature name="android.software.window_magnification" /> diff --git a/include/android/font.h b/include/android/font.h index 8a3a474f25..022572535b 100644 --- a/include/android/font.h +++ b/include/android/font.h @@ -31,6 +31,7 @@ #include <stdbool.h> #include <stddef.h> +#include <stdint.h> #include <sys/cdefs.h> /****************************************************************** @@ -86,10 +87,11 @@ enum { AFONT_WEIGHT_MAX = 1000 }; +struct AFont; /** * AFont provides information of the single font configuration. */ -struct AFont; +typedef struct AFont AFont; /** * Close an AFont. diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h index 4417422687..60ff95e123 100644 --- a/include/android/font_matcher.h +++ b/include/android/font_matcher.h @@ -75,6 +75,7 @@ #include <stdbool.h> #include <stddef.h> +#include <stdint.h> #include <sys/cdefs.h> #include <android/font.h> @@ -116,11 +117,12 @@ enum { AFAMILY_VARIANT_ELEGANT = 2, }; +struct AFontMatcher; /** * AFontMatcher performs match operation on given parameters and available font files. * This matcher is not a thread-safe object. Do not pass this matcher to other threads. */ -struct AFontMatcher; +typedef struct AFontMatcher AFontMatcher; /** * Select the best font from given parameters. diff --git a/include/android/input.h b/include/android/input.h index 38b27bc587..5d19c5cb13 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -54,7 +54,14 @@ #include <stdint.h> #include <sys/types.h> #include <android/keycodes.h> + +// This file is included by modules that have host support but android/looper.h is not supported +// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled. +#ifndef __BIONIC__ +#define __REMOVED_IN(x) __attribute__((deprecated)) +#endif #include <android/looper.h> + #include <jni.h> #if !defined(__INTRODUCED_IN) @@ -764,9 +771,33 @@ enum { * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_16 = 47, + /** + * Axis constant: X gesture offset axis of a motion event. + * + * - For a touch pad, reports the distance that a swipe gesture has moved in the X axis, as a + * proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a + * swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of + * -0.1. + */ + AMOTION_EVENT_AXIS_GESTURE_X_OFFSET = 48, + /** + * Axis constant: Y gesture offset axis of a motion event. + * + * The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis. + */ + AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET = 49, + + /** + * Note: This is not an "Axis constant". It does not represent any axis, nor should it be used + * to represent any axis. It is a constant holding the value of the largest defined axis value, + * to make some computations (like iterating through all possible axes) cleaner. + * Please update the value accordingly if you add a new axis. + */ + AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. + // Update AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE accordingly as well. }; /** @@ -833,6 +864,12 @@ enum AMotionClassification : uint32_t { * This classification type should be used to accelerate the long press behaviour. */ AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2, + /** + * Classification constant: touchpad two-finger swipe. + * + * The current event stream represents the user swiping with two fingers on a touchpad. + */ + AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3, }; /** diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 214559d683..e5b5db2964 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -776,7 +776,59 @@ enum { AKEYCODE_THUMBS_DOWN = 287, /** Used to switch current account that is consuming content. * May be consumed by system to switch current viewer profile. */ - AKEYCODE_PROFILE_SWITCH = 288 + AKEYCODE_PROFILE_SWITCH = 288, + /** Video Application key #1. */ + AKEYCODE_VIDEO_APP_1 = 289, + /** Video Application key #2. */ + AKEYCODE_VIDEO_APP_2 = 290, + /** Video Application key #3. */ + AKEYCODE_VIDEO_APP_3 = 291, + /** Video Application key #4. */ + AKEYCODE_VIDEO_APP_4 = 292, + /** Video Application key #5. */ + AKEYCODE_VIDEO_APP_5 = 293, + /** Video Application key #6. */ + AKEYCODE_VIDEO_APP_6 = 294, + /** Video Application key #7. */ + AKEYCODE_VIDEO_APP_7 = 295, + /** Video Application key #8. */ + AKEYCODE_VIDEO_APP_8 = 296, + /** Featured Application key #1. */ + AKEYCODE_FEATURED_APP_1 = 297, + /** Featured Application key #2. */ + AKEYCODE_FEATURED_APP_2 = 298, + /** Featured Application key #3. */ + AKEYCODE_FEATURED_APP_3 = 299, + /** Featured Application key #4. */ + AKEYCODE_FEATURED_APP_4 = 300, + /** Demo Application key #1. */ + AKEYCODE_DEMO_APP_1 = 301, + /** Demo Application key #2. */ + AKEYCODE_DEMO_APP_2 = 302, + /** Demo Application key #3. */ + AKEYCODE_DEMO_APP_3 = 303, + /** Demo Application key #4. */ + AKEYCODE_DEMO_APP_4 = 304, + /** Keyboard backlight Down key. + * Adjusts the keyboard backlight brightness down. */ + AKEYCODE_KEYBOARD_BACKLIGHT_DOWN = 305, + /** Keyboard backlight Up key. + * Adjusts the keyboard backlight brightness up. */ + AKEYCODE_KEYBOARD_BACKLIGHT_UP = 306, + /** Keyboard backlight Toggle key. + * Toggles the keyboard backlight on/off. */ + AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307, + /** The primary button on the barrel of a stylus. + * This is usually the button closest to the tip of the stylus. */ + AKEYCODE_STYLUS_BUTTON_PRIMARY = 308, + /** The secondary button on the barrel of a stylus. + * This is usually the second button from the tip of the stylus. */ + AKEYCODE_STYLUS_BUTTON_SECONDARY = 309, + /** The tertiary button on the barrel of a stylus. + * This is usually the third button from the tip of the stylus. */ + AKEYCODE_STYLUS_BUTTON_TERTIARY = 310, + /** A button on the tail end of a stylus. */ + AKEYCODE_STYLUS_BUTTON_TAIL = 311, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/looper.h b/include/android/looper.h index 718f703048..4fe142a8e2 100644 --- a/include/android/looper.h +++ b/include/android/looper.h @@ -201,8 +201,11 @@ int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa * Like ALooper_pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. + * + * Removed in API 34 as ALooper_pollAll can swallow ALooper_wake calls. + * Use ALooper_pollOnce instead. */ -int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) __REMOVED_IN(1); /** * Wakes the poll asynchronously. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 5fa47f64be..eed6b3339f 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -88,6 +88,36 @@ typedef struct APerformanceHintManager APerformanceHintManager; typedef struct APerformanceHintSession APerformanceHintSession; /** + * Hints for the session used by {@link APerformanceHint_sendHint} to signal upcoming changes + * in the mode or workload. + */ +enum SessionHint { + /** + * This hint indicates a sudden increase in CPU workload intensity. It means + * that this hint session needs extra CPU resources immediately to meet the + * target duration for the current work cycle. + */ + CPU_LOAD_UP = 0, + /** + * This hint indicates a decrease in CPU workload intensity. It means that + * this hint session can reduce CPU resources and still meet the target duration. + */ + CPU_LOAD_DOWN = 1, + /* + * This hint indicates an upcoming CPU workload that is completely changed and + * unknown. It means that the hint session should reset CPU resources to a known + * baseline to prepare for an arbitrary load, and must wake up if inactive. + */ + CPU_LOAD_RESET = 2, + /* + * This hint indicates that the most recent CPU workload is resuming after a + * period of inactivity. It means that the hint session should allocate similar + * CPU resources to what was used previously, and must wake up if inactive. + */ + CPU_LOAD_RESUME = 3, +}; + +/** * Acquire an instance of the performance hint manager. * * @return manager instance on success, nullptr on failure. @@ -159,6 +189,17 @@ int APerformanceHint_reportActualWorkDuration( void APerformanceHint_closeSession( APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); +/** + * Sends performance hints to inform the hint session of changes in the workload. + * + * @param session The performance hint session instance to update. + * @param hint The hint to send to the session. + * @return 0 on success + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_sendHint( + APerformanceHintSession* session, int hint) __INTRODUCED_IN(__ANDROID_API_U__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H diff --git a/include/android/sensor.h b/include/android/sensor.h index ba81bc8d8e..085fc270c1 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -45,6 +45,11 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ +// This file is included by modules that have host support but android/looper.h is not supported +// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled. +#ifndef __BIONIC__ +#define __REMOVED_IN(x) __attribute__((deprecated)) +#endif #include <android/looper.h> #include <stdbool.h> diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 6223ef7f82..f76e73d3cf 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -545,6 +545,8 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, * You can register for changes in the refresh rate using * \a AChoreographer_registerRefreshRateCallback. * + * See ASurfaceTransaction_clearFrameRate(). + * * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special * value that indicates the app will accept the system's choice for the display frame rate, which is * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to @@ -568,6 +570,31 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* tra __INTRODUCED_IN(31); /** + * Clears the frame rate which is set for \a surface_control. + * + * This is equivalent to calling + * ASurfaceTransaction_setFrameRateWithChangeStrategy( + * transaction, 0, compatibility, changeFrameRateStrategy). + * + * Usage of this API won't directly affect the application's frame production pipeline. However, + * because the system may change the display refresh rate, calls to this function may result in + * changes to Choreographer callback timings, and changes to the time interval at which the system + * releases buffers back to the application. + * + * See ASurfaceTransaction_setFrameRateWithChangeStrategy() + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ASurfaceTransaction_setFrameRateWithChangeStrategy(). + * + * Available since API level 34. + */ +void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control) + __INTRODUCED_IN(__ANDROID_API_U__); + +/** * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl. * * By default backpressure is disabled, which means submitting a buffer prior to receiving diff --git a/include/android/surface_control_jni.h b/include/android/surface_control_jni.h new file mode 100644 index 0000000000..840f6e724b --- /dev/null +++ b/include/android/surface_control_jni.h @@ -0,0 +1,68 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ + +/** + * @file surface_control_jni.h + */ + +#ifndef ANDROID_SURFACE_CONTROL_JNI_H +#define ANDROID_SURFACE_CONTROL_JNI_H + +#include <jni.h> +#include <sys/cdefs.h> + +#include <android/surface_control.h> + +__BEGIN_DECLS + +/** + * Return the ASurfaceControl wrapped by a Java SurfaceControl object. + * + * The caller takes ownership of the returned ASurfaceControl returned and must + * release it * using ASurfaceControl_release. + * + * surfaceControlObj must be a non-null instance of android.view.SurfaceControl + * and isValid() must be true. + * + * Available since API level 34. + */ +ASurfaceControl* _Nonnull ASurfaceControl_fromJava(JNIEnv* _Nonnull env, + jobject _Nonnull surfaceControlObj) __INTRODUCED_IN(__ANDROID_API_U__); + +/** + * Return the ASurfaceTransaction wrapped by a Java Transaction object. + * + * The returned ASurfaceTransaction is still owned by the Java Transaction object is only + * valid while the Java Transaction object is alive. In particular, the returned transaction + * must NOT be deleted with ASurfaceTransaction_delete. + * + * transactionObj must be a non-null instance of + * android.view.SurfaceControl.Transaction and close() must not already be called. + * + * Available since API level 34. + */ +ASurfaceTransaction* _Nonnull ASurfaceTransaction_fromJava(JNIEnv* _Nonnull env, + jobject _Nonnull transactionObj) __INTRODUCED_IN(__ANDROID_API_U__); + +__END_DECLS + +#endif // ANDROID_SURFACE_CONTROL_JNI_H +/** @} */ diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h index b0bbb954a9..94484eaf54 100644 --- a/include/android/system_fonts.h +++ b/include/android/system_fonts.h @@ -87,13 +87,14 @@ __BEGIN_DECLS +struct ASystemFontIterator; /** * ASystemFontIterator provides access to the system font configuration. * * ASystemFontIterator is an iterator for all available system font settings. * This iterator is not a thread-safe object. Do not pass this iterator to other threads. */ -struct ASystemFontIterator; +typedef struct ASystemFontIterator ASystemFontIterator; /** * Create a system font iterator. diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h index 4aa2f60d45..6794fbfc34 100644 --- a/include/audiomanager/AudioManager.h +++ b/include/audiomanager/AudioManager.h @@ -38,8 +38,52 @@ typedef enum { PLAYER_STATE_PAUSED = 3, PLAYER_STATE_STOPPED = 4, PLAYER_UPDATE_DEVICE_ID = 5, + PLAYER_UPDATE_PORT_ID = 6, + PLAYER_UPDATE_MUTED = 7, } player_state_t; +static constexpr char + kExtraPlayerEventMuteKey[] = "android.media.extra.PLAYER_EVENT_MUTE"; +enum { + PLAYER_MUTE_MASTER = (1 << 0), + PLAYER_MUTE_STREAM_VOLUME = (1 << 1), + PLAYER_MUTE_STREAM_MUTED = (1 << 2), + PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3), + PLAYER_MUTE_CLIENT_VOLUME = (1 << 4), + PLAYER_MUTE_VOLUME_SHAPER = (1 << 5), +}; + +struct mute_state_t { + /** Flag used when the master volume is causing the mute state. */ + bool muteFromMasterMute = false; + /** Flag used when the stream volume is causing the mute state. */ + bool muteFromStreamVolume = false; + /** Flag used when the stream muted is causing the mute state. */ + bool muteFromStreamMuted = false; + /** Flag used when playback is restricted by AppOps manager with OP_PLAY_AUDIO. */ + bool muteFromPlaybackRestricted = false; + /** Flag used when audio track was muted by client volume. */ + bool muteFromClientVolume = false; + /** Flag used when volume is muted by volume shaper. */ + bool muteFromVolumeShaper = false; + + explicit operator int() const + { + int result = muteFromMasterMute * PLAYER_MUTE_MASTER; + result |= muteFromStreamVolume * PLAYER_MUTE_STREAM_VOLUME; + result |= muteFromStreamMuted * PLAYER_MUTE_STREAM_MUTED; + result |= muteFromPlaybackRestricted * PLAYER_MUTE_PLAYBACK_RESTRICTED; + result |= muteFromClientVolume * PLAYER_MUTE_CLIENT_VOLUME; + result |= muteFromVolumeShaper * PLAYER_MUTE_VOLUME_SHAPER; + return result; + } + + bool operator==(const mute_state_t& other) const + { + return static_cast<int>(*this) == static_cast<int>(other); + } +}; + // must be kept in sync with definitions in AudioManager.java #define RECORD_RIID_INVALID -1 diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h index 426e10c9bc..769670ea99 100644 --- a/include/audiomanager/IAudioManager.h +++ b/include/audiomanager/IAudioManager.h @@ -17,8 +17,10 @@ #ifndef ANDROID_IAUDIOMANAGER_H #define ANDROID_IAUDIOMANAGER_H +#include <audiomanager/AudioManager.h> #include <utils/Errors.h> #include <binder/IInterface.h> +#include <binder/PersistableBundle.h> #include <hardware/power.h> #include <system/audio.h> @@ -40,6 +42,7 @@ public: RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5, RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6, PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7, + PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 8, }; DECLARE_META_INTERFACE(AudioManager) @@ -52,12 +55,14 @@ public: /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage, audio_content_type_t content)= 0; /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, - audio_port_handle_t deviceId) = 0; + audio_port_handle_t eventId) = 0; /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0; virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0; /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0; /*oneway*/ virtual status_t releaseRecorder(audio_unique_id_t riid) = 0; /*oneway*/ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) = 0; + /*oneway*/ virtual status_t portEvent(audio_port_handle_t portId, player_state_t event, + const std::unique_ptr<os::PersistableBundle>& extras) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h new file mode 100644 index 0000000000..c5ff03b80d --- /dev/null +++ b/include/ftl/algorithm.h @@ -0,0 +1,71 @@ +/* + * Copyright 2022 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 <algorithm> +#include <functional> +#include <utility> + +#include <ftl/optional.h> + +namespace android::ftl { + +// Adapter for std::find_if that converts the return value from iterator to optional. +// +// const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; +// assert(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }) == "cake"sv); +// +template <typename Container, typename Predicate, typename V = typename Container::value_type> +constexpr auto find_if(const Container& container, Predicate&& predicate) + -> Optional<std::reference_wrapper<const V>> { + const auto it = std::find_if(std::cbegin(container), std::cend(container), + std::forward<Predicate>(predicate)); + if (it == std::cend(container)) return {}; + return std::cref(*it); +} + +// Transformers for ftl::find_if on a map-like `Container` that contains key-value pairs. +// +// const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>( +// 12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv); +// +// using Map = decltype(map); +// +// assert(14 == ftl::find_if(map, [](const auto& pair) { +// return pair.second.size() == 3; +// }).transform(ftl::to_key<Map>)); +// +// const auto opt = ftl::find_if(map, [](const auto& pair) { +// return pair.second.size() == 1; +// }).transform(ftl::to_mapped_ref<Map>); +// +// assert(opt); +// assert(opt->get() == ftl::StaticVector("tiramisu"sv)); +// +template <typename Map, typename Pair = typename Map::value_type, + typename Key = typename Map::key_type> +constexpr auto to_key(const Pair& pair) -> Key { + return pair.first; +} + +template <typename Map, typename Pair = typename Map::value_type, + typename Mapped = typename Map::mapped_type> +constexpr auto to_mapped_ref(const Pair& pair) -> std::reference_wrapper<const Mapped> { + return std::cref(pair.second); +} + +} // namespace android::ftl diff --git a/include/ftl/concat.h b/include/ftl/concat.h index ded48f7c8c..e0774d39f3 100644 --- a/include/ftl/concat.h +++ b/include/ftl/concat.h @@ -20,7 +20,9 @@ namespace android::ftl { -// Lightweight (not allocating nor sprintf-based) concatenation. +// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be +// values of integral type (including bool and char), string literals, or strings whose length +// is constrained: // // std::string_view name = "Volume"; // ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB"); diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h index 8ce949ef05..726ba0297e 100644 --- a/include/ftl/details/concat.h +++ b/include/ftl/details/concat.h @@ -19,6 +19,7 @@ #include <functional> #include <string_view> +#include <ftl/details/type_traits.h> #include <ftl/string.h> namespace android::ftl::details { @@ -26,16 +27,42 @@ namespace android::ftl::details { template <typename T, typename = void> struct StaticString; +// Booleans. template <typename T> -struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> { - static constexpr std::size_t N = to_chars_length_v<T>; +struct StaticString<T, std::enable_if_t<is_bool_v<T>>> { + static constexpr std::size_t N = 5; // Length of "false". - explicit StaticString(T v) : view(to_chars(buffer, v)) {} + explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {} - to_chars_buffer_t<T> buffer; const std::string_view view; }; +// Characters. +template <typename T> +struct StaticString<T, std::enable_if_t<is_char_v<T>>> { + static constexpr std::size_t N = 1; + + explicit constexpr StaticString(char c) : character(c) {} + + const char character; + const std::string_view view{&character, 1u}; +}; + +// Integers, including the integer value of other character types like char32_t. +template <typename T> +struct StaticString< + T, std::enable_if_t<std::is_integral_v<remove_cvref_t<T>> && !is_bool_v<T> && !is_char_v<T>>> { + using U = remove_cvref_t<T>; + static constexpr std::size_t N = to_chars_length_v<U>; + + // TODO: Mark this and to_chars as `constexpr` in C++23. + explicit StaticString(U v) : view(to_chars(buffer, v)) {} + + to_chars_buffer_t<U> buffer; + const std::string_view view; +}; + +// Character arrays. template <std::size_t M> struct StaticString<const char (&)[M], void> { static constexpr std::size_t N = M - 1; @@ -50,6 +77,7 @@ struct Truncated { std::string_view view; }; +// Strings with constrained length. template <std::size_t M> struct StaticString<Truncated<M>, void> { static constexpr std::size_t N = M; diff --git a/include/ftl/details/match.h b/include/ftl/details/match.h new file mode 100644 index 0000000000..51b99d2f13 --- /dev/null +++ b/include/ftl/details/match.h @@ -0,0 +1,59 @@ +/* + * Copyright 2022 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 <type_traits> +#include <variant> + +namespace android::ftl::details { + +template <typename... Ms> +struct Matcher : Ms... { + using Ms::operator()...; +}; + +// Deduction guide. +template <typename... Ms> +Matcher(Ms...) -> Matcher<Ms...>; + +template <typename Matcher, typename... Ts> +constexpr bool is_exhaustive_match_v = (std::is_invocable_v<Matcher, Ts> && ...); + +template <typename...> +struct Match; + +template <typename T, typename U, typename... Ts> +struct Match<T, U, Ts...> { + template <typename Variant, typename Matcher> + static decltype(auto) match(Variant& variant, const Matcher& matcher) { + if (auto* const ptr = std::get_if<T>(&variant)) { + return matcher(*ptr); + } else { + return Match<U, Ts...>::match(variant, matcher); + } + } +}; + +template <typename T> +struct Match<T> { + template <typename Variant, typename Matcher> + static decltype(auto) match(Variant& variant, const Matcher& matcher) { + return matcher(std::get<T>(variant)); + } +}; + +} // namespace android::ftl::details diff --git a/include/ftl/details/mixins.h b/include/ftl/details/mixins.h new file mode 100644 index 0000000000..9ab9e083ae --- /dev/null +++ b/include/ftl/details/mixins.h @@ -0,0 +1,30 @@ +/* + * Copyright 2022 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 + +namespace android::ftl::details { + +template <typename Self, template <typename> class> +class Mixin { + protected: + constexpr Self& self() { return *static_cast<Self*>(this); } + constexpr const Self& self() const { return *static_cast<const Self*>(this); } + + constexpr auto& mut() { return self().value_; } +}; + +} // namespace android::ftl::details diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h new file mode 100644 index 0000000000..bff7c1e000 --- /dev/null +++ b/include/ftl/details/optional.h @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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 <functional> +#include <optional> + +#include <ftl/details/type_traits.h> + +namespace android::ftl { + +template <typename> +struct Optional; + +namespace details { + +template <typename> +struct is_optional : std::false_type {}; + +template <typename T> +struct is_optional<std::optional<T>> : std::true_type {}; + +template <typename T> +struct is_optional<Optional<T>> : std::true_type {}; + +template <typename F, typename T> +struct transform_result { + using type = Optional<std::remove_cv_t<std::invoke_result_t<F, T>>>; +}; + +template <typename F, typename T> +using transform_result_t = typename transform_result<F, T>::type; + +template <typename F, typename T> +struct and_then_result { + using type = remove_cvref_t<std::invoke_result_t<F, T>>; + static_assert(is_optional<type>{}, "and_then function must return an optional"); +}; + +template <typename F, typename T> +using and_then_result_t = typename and_then_result<F, T>::type; + +} // namespace details +} // namespace android::ftl diff --git a/include/ftl/details/type_traits.h b/include/ftl/details/type_traits.h new file mode 100644 index 0000000000..47bebc5114 --- /dev/null +++ b/include/ftl/details/type_traits.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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 <type_traits> + +namespace android::ftl::details { + +// TODO: Replace with std::remove_cvref_t in C++20. +template <typename U> +using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>; + +template <typename T> +constexpr bool is_bool_v = std::is_same_v<remove_cvref_t<T>, bool>; + +template <typename T> +constexpr bool is_char_v = std::is_same_v<remove_cvref_t<T>, char>; + +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h index 82af1d6cf8..075d12bd17 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -92,7 +92,7 @@ inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; // enum class E { A, B, C }; // static_assert(ftl::to_underlying(E::B) == 1); // -template <typename E> +template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> constexpr auto to_underlying(E v) { return static_cast<std::underlying_type_t<E>>(v); } diff --git a/include/ftl/flags.h b/include/ftl/flags.h index 70aaa0e6dd..cdb4e840a4 100644 --- a/include/ftl/flags.h +++ b/include/ftl/flags.h @@ -125,7 +125,7 @@ public: /* Tests whether all of the given flags are set */ bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; } - Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); } + constexpr Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); } Flags<F>& operator|=(Flags<F> rhs) { mFlags = mFlags | rhs.mFlags; return *this; @@ -217,7 +217,7 @@ inline Flags<F> operator~(F f) { } template <typename F, typename = std::enable_if_t<is_scoped_enum_v<F>>> -Flags<F> operator|(F lhs, F rhs) { +constexpr Flags<F> operator|(F lhs, F rhs) { return static_cast<F>(to_underlying(lhs) | to_underlying(rhs)); } diff --git a/include/ftl/match.h b/include/ftl/match.h new file mode 100644 index 0000000000..7318c45b7b --- /dev/null +++ b/include/ftl/match.h @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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 <utility> +#include <variant> + +#include <ftl/details/match.h> + +namespace android::ftl { + +// Concise alternative to std::visit that compiles to branches rather than a dispatch table. For +// std::variant<T0, ..., TN> where N is small, this is slightly faster since the branches can be +// inlined unlike the function pointers. +// +// using namespace std::chrono; +// std::variant<seconds, minutes, hours> duration = 119min; +// +// // Mutable match. +// ftl::match(duration, [](auto& d) { ++d; }); +// +// // Immutable match. Exhaustive due to minutes being convertible to seconds. +// assert("2 hours"s == +// ftl::match(duration, +// [](const seconds& s) { +// const auto h = duration_cast<hours>(s); +// return std::to_string(h.count()) + " hours"s; +// }, +// [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; })); +// +template <typename... Ts, typename... Ms> +decltype(auto) match(std::variant<Ts...>& variant, Ms&&... matchers) { + const auto matcher = details::Matcher{std::forward<Ms>(matchers)...}; + static_assert(details::is_exhaustive_match_v<decltype(matcher), Ts&...>, "Non-exhaustive match"); + + return details::Match<Ts...>::match(variant, matcher); +} + +template <typename... Ts, typename... Ms> +decltype(auto) match(const std::variant<Ts...>& variant, Ms&&... matchers) { + const auto matcher = details::Matcher{std::forward<Ms>(matchers)...}; + static_assert(details::is_exhaustive_match_v<decltype(matcher), const Ts&...>, + "Non-exhaustive match"); + + return details::Match<Ts...>::match(variant, matcher); +} + +} // namespace android::ftl diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h new file mode 100644 index 0000000000..0e1d2004a3 --- /dev/null +++ b/include/ftl/mixins.h @@ -0,0 +1,148 @@ +/* + * Copyright 2022 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 <ftl/details/mixins.h> + +namespace android::ftl { + +// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common +// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must +// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic, +// etc.) are enabled through inheritance: +// +// struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> { +// using Constructible::Constructible; +// }; +// +// static_assert(!std::is_default_constructible_v<Id>); +// +// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is +// zero-initialized unless specified: +// +// struct Color : ftl::DefaultConstructible<Color, std::uint8_t>, +// ftl::Equatable<Color>, +// ftl::Orderable<Color> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// static_assert(Color() == Color(0u)); +// static_assert(ftl::to_underlying(Color(-1)) == 255u); +// static_assert(Color(1u) < Color(2u)); +// +// struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>, +// ftl::Equatable<Sequence>, +// ftl::Orderable<Sequence>, +// ftl::Incrementable<Sequence> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// static_assert(Sequence() == Sequence(-1)); +// +// The underlying type need not be a fundamental type: +// +// struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>, +// ftl::Equatable<Timeout>, +// ftl::Addable<Timeout> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// using namespace std::chrono_literals; +// static_assert(Timeout() + Timeout(5s) == Timeout(15s)); +// +template <typename Self, typename T> +struct Constructible { + explicit constexpr Constructible(T value) : value_(value) {} + + explicit constexpr operator const T&() const { return value_; } + + private: + template <typename, template <typename> class> + friend class details::Mixin; + + T value_; +}; + +template <typename Self, typename T, auto kDefault = T{}> +struct DefaultConstructible : Constructible<Self, T> { + using Constructible<Self, T>::Constructible; + constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} +}; + +// Shorthand for casting a type-safe wrapper to its underlying value. +template <typename Self, typename T> +constexpr const T& to_underlying(const Constructible<Self, T>& c) { + return static_cast<const T&>(c); +} + +// Comparison operators for equality. +template <typename Self> +struct Equatable : details::Mixin<Self, Equatable> { + constexpr bool operator==(const Self& other) const { + return to_underlying(this->self()) == to_underlying(other); + } + + constexpr bool operator!=(const Self& other) const { return !(*this == other); } +}; + +// Comparison operators for ordering. +template <typename Self> +struct Orderable : details::Mixin<Self, Orderable> { + constexpr bool operator<(const Self& other) const { + return to_underlying(this->self()) < to_underlying(other); + } + + constexpr bool operator>(const Self& other) const { return other < this->self(); } + constexpr bool operator>=(const Self& other) const { return !(*this < other); } + constexpr bool operator<=(const Self& other) const { return !(*this > other); } +}; + +// Pre-increment and post-increment operators. +template <typename Self> +struct Incrementable : details::Mixin<Self, Incrementable> { + constexpr Self& operator++() { + ++this->mut(); + return this->self(); + } + + constexpr Self operator++(int) { + const Self tmp = this->self(); + operator++(); + return tmp; + } +}; + +// Additive operators, including incrementing. +template <typename Self> +struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> { + constexpr Self& operator+=(const Self& other) { + this->mut() += to_underlying(other); + return this->self(); + } + + constexpr Self operator+(const Self& other) const { + Self tmp = this->self(); + return tmp += other; + } + + private: + using Base = details::Mixin<Self, Addable>; + using Base::mut; + using Base::self; +}; + +} // namespace android::ftl diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h new file mode 100644 index 0000000000..35d09d71de --- /dev/null +++ b/include/ftl/non_null.h @@ -0,0 +1,116 @@ +/* + * Copyright 2022 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 <cstdlib> +#include <type_traits> +#include <utility> + +namespace android::ftl { + +// Enforces and documents non-null pre/post-condition for (raw or smart) pointers. +// +// void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr, +// ftl::NonNull<std::size_t*> length_ptr) { +// // No need for `nullptr` checks. +// *length_ptr = string_ptr->length(); +// } +// +// const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android")); +// std::size_t size; +// get_length(string_ptr, ftl::as_non_null(&size)); +// assert(size == 7u); +// +// For compatibility with std::unique_ptr<T> and performance with std::shared_ptr<T>, move +// operations are allowed despite breaking the invariant: +// +// using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>; +// +// Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) { +// // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point. +// auto unique_ptr = std::move(non_null_ptr).take(); +// +// auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr))); +// auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr; +// +// return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)}; +// } +// +// auto ptr = ftl::as_non_null(std::make_unique<int>(42)); +// const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true); +// assert(ptr1.get() == ptr2); +// +template <typename Pointer> +class NonNull final { + struct Passkey {}; + + public: + // Disallow `nullptr` explicitly for clear compilation errors. + NonNull() = delete; + NonNull(std::nullptr_t) = delete; + + // Copy operations. + + constexpr NonNull(const NonNull&) = default; + constexpr NonNull& operator=(const NonNull&) = default; + + constexpr const Pointer& get() const { return pointer_; } + constexpr explicit operator const Pointer&() const { return get(); } + + // Move operations. These break the invariant, so care must be taken to avoid subsequent access. + + constexpr NonNull(NonNull&&) = default; + constexpr NonNull& operator=(NonNull&&) = default; + + constexpr Pointer take() && { return std::move(pointer_); } + constexpr explicit operator Pointer() && { return take(); } + + // Dereferencing. + constexpr decltype(auto) operator*() const { return *get(); } + constexpr decltype(auto) operator->() const { return get(); } + + // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions + // through the passkey idiom, for clear compilation errors. + template <typename P> + constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) { + if (!pointer_) std::abort(); + } + + private: + template <typename P> + friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>; + + Pointer pointer_; +}; + +template <typename P> +constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { + using Passkey = typename NonNull<std::decay_t<P>>::Passkey; + return {Passkey{}, std::forward<P>(pointer)}; +} + +template <typename P, typename Q> +constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) { + return lhs.get() == rhs.get(); +} + +template <typename P, typename Q> +constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { + return !operator==(lhs, rhs); +} + +} // namespace android::ftl diff --git a/include/ftl/optional.h b/include/ftl/optional.h new file mode 100644 index 0000000000..a818128c92 --- /dev/null +++ b/include/ftl/optional.h @@ -0,0 +1,125 @@ +/* + * Copyright 2022 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 <functional> +#include <optional> +#include <utility> + +#include <ftl/details/optional.h> + +namespace android::ftl { + +// Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8. +// +// TODO: Remove in C++23. +// +template <typename T> +struct Optional final : std::optional<T> { + using std::optional<T>::optional; + + // Implicit downcast. + Optional(std::optional<T> other) : std::optional<T>(std::move(other)) {} + + using std::optional<T>::has_value; + using std::optional<T>::value; + + // Returns Optional<U> where F is a function that maps T to U. + template <typename F> + constexpr auto transform(F&& f) const& { + using R = details::transform_result_t<F, decltype(value())>; + if (has_value()) return R(std::invoke(std::forward<F>(f), value())); + return R(); + } + + template <typename F> + constexpr auto transform(F&& f) & { + using R = details::transform_result_t<F, decltype(value())>; + if (has_value()) return R(std::invoke(std::forward<F>(f), value())); + return R(); + } + + template <typename F> + constexpr auto transform(F&& f) const&& { + using R = details::transform_result_t<F, decltype(std::move(value()))>; + if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value()))); + return R(); + } + + template <typename F> + constexpr auto transform(F&& f) && { + using R = details::transform_result_t<F, decltype(std::move(value()))>; + if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value()))); + return R(); + } + + // Returns Optional<U> where F is a function that maps T to Optional<U>. + template <typename F> + constexpr auto and_then(F&& f) const& { + using R = details::and_then_result_t<F, decltype(value())>; + if (has_value()) return std::invoke(std::forward<F>(f), value()); + return R(); + } + + template <typename F> + constexpr auto and_then(F&& f) & { + using R = details::and_then_result_t<F, decltype(value())>; + if (has_value()) return std::invoke(std::forward<F>(f), value()); + return R(); + } + + template <typename F> + constexpr auto and_then(F&& f) const&& { + using R = details::and_then_result_t<F, decltype(std::move(value()))>; + if (has_value()) return std::invoke(std::forward<F>(f), std::move(value())); + return R(); + } + + template <typename F> + constexpr auto and_then(F&& f) && { + using R = details::and_then_result_t<F, decltype(std::move(value()))>; + if (has_value()) return std::invoke(std::forward<F>(f), std::move(value())); + return R(); + } + + // Delete new for this class. Its base doesn't have a virtual destructor, and + // if it got deleted via base class pointer, it would cause undefined + // behavior. There's not a good reason to allocate this object on the heap + // anyway. + static void* operator new(size_t) = delete; + static void* operator new[](size_t) = delete; + +}; + +template <typename T, typename U> +constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) { + return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs); +} + +template <typename T, typename U> +constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) { + return !(lhs == rhs); +} + +// Deduction guides. +template <typename T> +Optional(T) -> Optional<T>; + +template <typename T> +Optional(std::optional<T>) -> Optional<T>; + +} // namespace android::ftl diff --git a/include/ftl/shared_mutex.h b/include/ftl/shared_mutex.h new file mode 100644 index 0000000000..146f5ba4a9 --- /dev/null +++ b/include/ftl/shared_mutex.h @@ -0,0 +1,47 @@ +/* + * Copyright 2022 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 <shared_mutex> + +namespace android::ftl { + +// Wrapper around std::shared_mutex to provide capabilities for thread-safety +// annotations. +// TODO(b/257958323): This class is no longer needed once b/135688034 is fixed (currently blocked on +// b/175635923). +class [[clang::capability("shared_mutex")]] SharedMutex final { + public: + [[clang::acquire_capability()]] void lock() { + mutex_.lock(); + } + [[clang::release_capability()]] void unlock() { + mutex_.unlock(); + } + + [[clang::acquire_shared_capability()]] void lock_shared() { + mutex_.lock_shared(); + } + [[clang::release_shared_capability()]] void unlock_shared() { + mutex_.unlock_shared(); + } + + private: + std::shared_mutex mutex_; +}; + +} // namespace android::ftl diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h index 5217e76064..49cde7fedc 100644 --- a/include/ftl/small_map.h +++ b/include/ftl/small_map.h @@ -17,11 +17,11 @@ #pragma once #include <ftl/initializer_list.h> +#include <ftl/optional.h> #include <ftl/small_vector.h> #include <algorithm> #include <functional> -#include <optional> #include <type_traits> #include <utility> @@ -47,7 +47,7 @@ namespace android::ftl { // assert(!map.dynamic()); // // assert(map.contains(123)); -// assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u); +// assert(map.get(42).transform([](const std::string& s) { return s.size(); }) == 3u); // // const auto opt = map.get(-1); // assert(opt); @@ -59,7 +59,7 @@ namespace android::ftl { // map.emplace_or_replace(0, "vanilla", 2u, 3u); // assert(map.dynamic()); // -// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); +// assert(map == SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv))); // template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>> class SmallMap final { @@ -123,9 +123,7 @@ class SmallMap final { const_iterator cend() const { return map_.cend(); } // Returns whether a mapping exists for the given key. - bool contains(const key_type& key) const { - return get(key, [](const mapped_type&) {}); - } + bool contains(const key_type& key) const { return get(key).has_value(); } // Returns a reference to the value for the given key, or std::nullopt if the key was not found. // @@ -139,46 +137,24 @@ class SmallMap final { // ref.get() = 'D'; // assert(d == 'D'); // - auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> { - return get(key, [](const mapped_type& v) { return std::cref(v); }); - } - - auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> { - return get(key, [](mapped_type& v) { return std::ref(v); }); + auto get(const key_type& key) const -> Optional<std::reference_wrapper<const mapped_type>> { + for (const auto& [k, v] : *this) { + if (KeyEqual{}(k, key)) { + return std::cref(v); + } + } + return {}; } - // Returns the result R of a unary operation F on (a constant or mutable reference to) the value - // for the given key, or std::nullopt if the key was not found. If F has a return type of void, - // then the Boolean result indicates whether the key was found. - // - // ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - // - // assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z'); - // assert(map.get('c', [](char& c) { c = std::toupper(c); })); - // - template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>> - auto get(const key_type& key, F f) const - -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> { + auto get(const key_type& key) -> Optional<std::reference_wrapper<mapped_type>> { for (auto& [k, v] : *this) { if (KeyEqual{}(k, key)) { - if constexpr (std::is_void_v<R>) { - f(v); - return true; - } else { - return f(v); - } + return std::ref(v); } } - return {}; } - template <typename F> - auto get(const key_type& key, F f) { - return std::as_const(*this).get( - key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); }); - } - // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); } iterator find(const key_type& key) { return find(key, begin()); } @@ -286,7 +262,7 @@ bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs for (const auto& [k, v] : lhs) { const auto& lv = v; - if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) { + if (!rhs.get(k).transform([&lv](const W& rv) { return lv == rv; }).value_or(false)) { return false; } } diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index 339726e4ea..11294c3ac8 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -21,11 +21,12 @@ #include <algorithm> #include <iterator> -#include <type_traits> #include <utility> #include <variant> #include <vector> +#include <ftl/details/type_traits.h> + namespace android::ftl { template <typename> @@ -80,10 +81,6 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma using Static = StaticVector<T, N>; using Dynamic = SmallVector<T, 0>; - // TODO: Replace with std::remove_cvref_t in C++20. - template <typename U> - using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>; - public: FTL_ARRAY_TRAIT(T, value_type); FTL_ARRAY_TRAIT(T, size_type); @@ -104,7 +101,7 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma // Constructs at most N elements. See StaticVector for underlying constructors. template <typename Arg, typename... Args, - typename = std::enable_if_t<!is_small_vector<remove_cvref_t<Arg>>{}>> + typename = std::enable_if_t<!is_small_vector<details::remove_cvref_t<Arg>>{}>> SmallVector(Arg&& arg, Args&&... args) : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {} diff --git a/include/ftl/unit.h b/include/ftl/unit.h new file mode 100644 index 0000000000..e38230b976 --- /dev/null +++ b/include/ftl/unit.h @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 <type_traits> +#include <utility> + +namespace android::ftl { + +// The unit type, and its only value. +constexpr struct Unit { +} unit; + +constexpr bool operator==(Unit, Unit) { + return true; +} + +constexpr bool operator!=(Unit, Unit) { + return false; +} + +// Adapts a function object F to return Unit. The return value of F is ignored. +// +// As a practical use, the function passed to ftl::Optional<T>::transform is not allowed to return +// void (cf. https://wg21.link/P0798R8#mapping-functions-returning-void), but may return Unit if +// only its side effects are meaningful: +// +// ftl::Optional opt = "food"s; +// opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); })); +// assert(opt == "foo"s); +// +template <typename F> +struct UnitFn { + F f; + + template <typename... Args> + Unit operator()(Args&&... args) { + return f(std::forward<Args>(args)...), unit; + } +}; + +template <typename F> +constexpr auto unit_fn(F&& f) -> UnitFn<std::decay_t<F>> { + return {std::forward<F>(f)}; +} + +} // namespace android::ftl diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 9148fee532..7457496784 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H -#define _LIBINPUT_DISPLAY_VIEWPORT_H +#pragma once #include <android-base/stringprintf.h> #include <ftl/enum.h> #include <ftl/string.h> #include <gui/constants.h> #include <input/Input.h> +#include <ui/Rotation.h> #include <cinttypes> #include <optional> @@ -30,13 +30,6 @@ using android::base::StringPrintf; namespace android { -enum { - DISPLAY_ORIENTATION_0 = 0, - DISPLAY_ORIENTATION_90 = 1, - DISPLAY_ORIENTATION_180 = 2, - DISPLAY_ORIENTATION_270 = 3 -}; - /** * Describes the different type of viewports supported by input flinger. * Keep in sync with values in InputManagerService.java. @@ -55,7 +48,7 @@ enum class ViewportType : int32_t { */ struct DisplayViewport { int32_t displayId; // -1 if invalid - int32_t orientation; + ui::Rotation orientation; int32_t logicalLeft; int32_t logicalTop; int32_t logicalRight; @@ -75,7 +68,7 @@ struct DisplayViewport { DisplayViewport() : displayId(ADISPLAY_ID_NONE), - orientation(DISPLAY_ORIENTATION_0), + orientation(ui::ROTATION_0), logicalLeft(0), logicalTop(0), logicalRight(0), @@ -112,7 +105,7 @@ struct DisplayViewport { void setNonDisplayViewport(int32_t width, int32_t height) { displayId = ADISPLAY_ID_NONE; - orientation = DISPLAY_ORIENTATION_0; + orientation = ui::ROTATION_0; logicalLeft = 0; logicalTop = 0; logicalRight = width; @@ -144,5 +137,3 @@ struct DisplayViewport { }; } // namespace android - -#endif // _LIBINPUT_DISPLAY_VIEWPORT_H diff --git a/include/input/Input.h b/include/input/Input.h index 7ea297049b..015efdd064 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_INPUT_H -#define _LIBINPUT_INPUT_H +#pragma once #pragma GCC system_header @@ -210,6 +209,8 @@ std::string inputEventSourceToString(int32_t source); bool isFromSource(uint32_t source, uint32_t test); +bool isStylusToolType(uint32_t toolType); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -290,6 +291,10 @@ enum class MotionClassification : uint8_t { * The current gesture likely represents a user intentionally exerting force on the touchscreen. */ DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS, + /** + * The current gesture represents the user swiping with two fingers on a touchpad. + */ + TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE, }; /** @@ -363,7 +368,7 @@ struct PointerCoords { // Values of axes that are stored in this structure packed in order by axis id // for each axis that is present in the structure according to 'bits'. - float values[MAX_AXES]; + std::array<float, MAX_AXES> values; inline void clear() { BitSet64::clear(bits); @@ -403,7 +408,8 @@ struct PointerCoords { return !(*this == other); } - void copyFrom(const PointerCoords& other); + inline void copyFrom(const PointerCoords& other) { *this = other; } + PointerCoords& operator=(const PointerCoords&) = default; private: void tooManyAxes(int axis); @@ -571,7 +577,7 @@ public: inline const ui::Transform& getTransform() const { return mTransform; } - int getSurfaceRotation() const; + std::optional<ui::Rotation> getSurfaceRotation() const; inline float getXPrecision() const { return mXPrecision; } @@ -1061,6 +1067,46 @@ public: uint32_t seq; }; -} // namespace android +/* Pointer icon styles. + * Must match the definition in android.view.PointerIcon. + * + * Due to backwards compatibility and public api constraints, this is a duplicate (but type safe) + * definition of PointerIcon.java. + * + * TODO(b/235023317) move this definition to an aidl and statically assign to the below java public + * api values. + * + * WARNING: Keep these definitions in sync with + * frameworks/base/core/java/android/view/PointerIcon.java + */ +enum class PointerIconStyle : int32_t { + TYPE_CUSTOM = -1, + TYPE_NULL = 0, + TYPE_ARROW = 1000, + TYPE_CONTEXT_MENU = 1001, + TYPE_HAND = 1002, + TYPE_HELP = 1003, + TYPE_WAIT = 1004, + TYPE_CELL = 1006, + TYPE_CROSSHAIR = 1007, + TYPE_TEXT = 1008, + TYPE_VERTICAL_TEXT = 1009, + TYPE_ALIAS = 1010, + TYPE_COPY = 1011, + TYPE_NO_DROP = 1012, + TYPE_ALL_SCROLL = 1013, + TYPE_HORIZONTAL_DOUBLE_ARROW = 1014, + TYPE_VERTICAL_DOUBLE_ARROW = 1015, + TYPE_TOP_RIGHT_DOUBLE_ARROW = 1016, + TYPE_TOP_LEFT_DOUBLE_ARROW = 1017, + TYPE_ZOOM_IN = 1018, + TYPE_ZOOM_OUT = 1019, + TYPE_GRAB = 1020, + TYPE_GRABBING = 1021, + + TYPE_SPOT_HOVER = 2000, + TYPE_SPOT_TOUCH = 2001, + TYPE_SPOT_ANCHOR = 2002, +}; -#endif // _LIBINPUT_INPUT_H +} // namespace android diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 3585392c2b..e911734407 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -14,15 +14,18 @@ * limitations under the License. */ -#ifndef _LIBINPUT_INPUT_DEVICE_H -#define _LIBINPUT_INPUT_DEVICE_H +#pragma once #include <android/sensor.h> +#include <ftl/flags.h> #include <input/Input.h> #include <input/KeyCharacterMap.h> #include <unordered_map> #include <vector> +#include <android/os/IInputConstants.h> +#include "android/hardware/input/InputDeviceCountryCode.h" + namespace android { /* @@ -55,6 +58,9 @@ struct InputDeviceIdentifier { // reuse values that are not associated with an input anymore. uint16_t nonce; + // The bluetooth address of the device, if known. + std::optional<std::string> bluetoothAddress; + /** * Return InputDeviceIdentifier.name that has been adjusted as follows: * - all characters besides alphanumerics, dash, @@ -104,12 +110,18 @@ enum class InputDeviceSensorReportingMode : int32_t { }; enum class InputDeviceLightType : int32_t { - MONO = 0, + INPUT = 0, PLAYER_ID = 1, - RGB = 2, - MULTI_COLOR = 3, + KEYBOARD_BACKLIGHT = 2, + + ftl_last = KEYBOARD_BACKLIGHT +}; - ftl_last = MULTI_COLOR +enum class InputDeviceLightCapability : uint32_t { + /** Capability to change brightness of the light */ + BRIGHTNESS = 0x00000001, + /** Capability to change color of the light */ + RGB = 0x00000002, }; struct InputDeviceSensorInfo { @@ -170,14 +182,17 @@ struct InputDeviceSensorInfo { struct InputDeviceLightInfo { explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type, + ftl::Flags<InputDeviceLightCapability> capabilityFlags, int32_t ordinal) - : name(name), id(id), type(type), ordinal(ordinal) {} + : name(name), id(id), type(type), capabilityFlags(capabilityFlags), ordinal(ordinal) {} // Name string of the light. std::string name; // Light id int32_t id; // Type of the light. InputDeviceLightType type; + // Light capabilities. + ftl::Flags<InputDeviceLightCapability> capabilityFlags; // Ordinal of the light int32_t ordinal; }; @@ -210,8 +225,10 @@ public: }; void initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, - bool hasMic); + const InputDeviceIdentifier& identifier, const std::string& alias, + bool isExternal, bool hasMic, + hardware::input::InputDeviceCountryCode countryCode = + hardware::input::InputDeviceCountryCode::INVALID); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } @@ -223,6 +240,7 @@ public: } inline bool isExternal() const { return mIsExternal; } inline bool hasMic() const { return mHasMic; } + inline hardware::input::InputDeviceCountryCode getCountryCode() const { return mCountryCode; } inline uint32_t getSources() const { return mSources; } const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; @@ -266,6 +284,9 @@ public: std::vector<InputDeviceLightInfo> getLights(); + inline void setSupportsUsi(bool supportsUsi) { mSupportsUsi = supportsUsi; } + inline bool supportsUsi() const { return mSupportsUsi; } + private: int32_t mId; int32_t mGeneration; @@ -274,9 +295,13 @@ private: std::string mAlias; bool mIsExternal; bool mHasMic; + hardware::input::InputDeviceCountryCode mCountryCode; uint32_t mSources; int32_t mKeyboardType; std::shared_ptr<KeyCharacterMap> mKeyCharacterMap; + // Whether this device supports the Universal Stylus Initiative (USI) protocol for styluses. + bool mSupportsUsi; + bool mHasVibrator; bool mHasBattery; bool mHasButtonUnderPad; @@ -325,6 +350,8 @@ extern std::string getInputDeviceConfigurationFilePathByName( const std::string& name, InputDeviceConfigurationFileType type); enum ReservedInputDeviceId : int32_t { + // Device id representing an invalid device + INVALID_INPUT_DEVICE_ID = android::os::IInputConstants::INVALID_INPUT_DEVICE_ID, // Device id of a special "virtual" keyboard that is always present. VIRTUAL_KEYBOARD_ID = -1, // Device id of the "built-in" keyboard if there is one. @@ -334,5 +361,3 @@ enum ReservedInputDeviceId : int32_t { }; } // namespace android - -#endif // _LIBINPUT_INPUT_DEVICE_H diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 2a742f9cf4..b4374acdcc 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_INPUT_EVENT_LABELS_H -#define _LIBINPUT_INPUT_EVENT_LABELS_H +#pragma once #include <input/Input.h> #include <android/keycodes.h> @@ -68,4 +67,3 @@ private: }; } // namespace android -#endif // _LIBINPUT_INPUT_EVENT_LABELS_H diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 5f9a37d69c..1c52792cf6 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_INPUT_TRANSPORT_H -#define _LIBINPUT_INPUT_TRANSPORT_H +#pragma once #pragma GCC system_header @@ -452,8 +451,11 @@ private: */ class InputConsumer { public: - /* Creates a consumer associated with an input channel. */ + /* Create a consumer associated with an input channel. */ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel); + /* Create a consumer associated with an input channel, override resampling system property */ + explicit InputConsumer(const std::shared_ptr<InputChannel>& channel, + bool enableTouchResampling); /* Destroys the consumer and releases its input channel. */ ~InputConsumer(); @@ -671,5 +673,3 @@ private: }; } // namespace android - -#endif // _LIBINPUT_INPUT_TRANSPORT_H diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index f6f8939b7a..867a08955c 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef _LIBINPUT_KEY_CHARACTER_MAP_H -#define _LIBINPUT_KEY_CHARACTER_MAP_H +#pragma once #include <stdint.h> +#include <list> #ifdef __linux__ #include <binder/IBinder.h> @@ -125,14 +125,21 @@ public: bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector<KeyEvent>& outEvents) const; + /* Maps an Android key code to another Android key code. This mapping is applied after scanCode + * and usageCodes are mapped to corresponding Android Keycode */ + void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + /* Maps a scan code and usage code to a key code, in case this key map overrides * the mapping in some way. */ status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const; - /* Tries to find a replacement key code for a given key code and meta state - * in character map. */ - void tryRemapKey(int32_t scanCode, int32_t metaState, - int32_t* outKeyCode, int32_t* outMetaState) const; + /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */ + int32_t applyKeyRemapping(int32_t fromKeyCode) const; + + /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file, + * that tries to find a replacement key code based on current meta state */ + std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode, + int32_t metaState) const; #ifdef __linux__ /* Reads a key map from a parcel. */ @@ -152,29 +159,22 @@ public: private: struct Behavior { - Behavior(); - Behavior(const Behavior& other); - - /* The next behavior in the list, or NULL if none. */ - Behavior* next; - /* The meta key modifiers for this behavior. */ - int32_t metaState; + int32_t metaState = 0; /* The character to insert. */ - char16_t character; + char16_t character = 0; /* The fallback keycode if the key is not handled. */ - int32_t fallbackKeyCode; + int32_t fallbackKeyCode = 0; /* The replacement keycode if the key has to be replaced outright. */ - int32_t replacementKeyCode; + int32_t replacementKeyCode = 0; }; struct Key { Key(); Key(const Key& other); - ~Key(); /* The single character label printed on the key, or 0 if none. */ char16_t label; @@ -184,7 +184,7 @@ private: /* The list of key behaviors sorted from most specific to least specific * meta key binding. */ - Behavior* firstBehavior; + std::list<Behavior> behaviors; }; class Parser { @@ -234,14 +234,14 @@ private: std::string mLoadFileName; bool mLayoutOverlayApplied; - KeyedVector<int32_t, int32_t> mKeysByScanCode; - KeyedVector<int32_t, int32_t> mKeysByUsageCode; + std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping; + std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode; + std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode; KeyCharacterMap(const std::string& filename); bool getKey(int32_t keyCode, const Key** outKey) const; - bool getKeyBehavior(int32_t keyCode, int32_t metaState, - const Key** outKey, const Behavior** outBehavior) const; + const Behavior* getKeyBehavior(int32_t keyCode, int32_t metaState) const; static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState); bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; @@ -277,5 +277,3 @@ private: }; } // namespace android - -#endif // _LIBINPUT_KEY_CHARACTER_MAP_H diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 1da78aa0c1..e203d190a6 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H -#define _LIBINPUT_KEY_LAYOUT_MAP_H +#pragma once #include <android-base/result.h> #include <stdint.h> @@ -78,7 +77,7 @@ public: std::optional<AxisInfo> mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code - base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode); + base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode) const; virtual ~KeyLayoutMap(); @@ -131,5 +130,3 @@ private: }; } // namespace android - -#endif // _LIBINPUT_KEY_LAYOUT_MAP_H diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 9a3e15f1cd..f7f960f8e6 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_KEYBOARD_H -#define _LIBINPUT_KEYBOARD_H +#pragma once #include <input/Input.h> #include <input/InputDevice.h> @@ -88,5 +87,3 @@ extern int32_t normalizeMetaState(int32_t oldMetaState); extern bool isMetaKey(int32_t keyCode); } // namespace android - -#endif // _LIBINPUT_KEYBOARD_H diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 55f730b287..e24344b3f1 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -24,16 +24,20 @@ namespace android { template <typename T> -std::string constToString(const T& v) { +inline std::string constToString(const T& v) { return std::to_string(v); } +inline std::string constToString(const std::string& s) { + return s; +} + /** * Convert an optional type to string. */ template <typename T> -std::string toString(const std::optional<T>& optional, - std::string (*toString)(const T&) = constToString) { +inline std::string toString(const std::optional<T>& optional, + std::string (*toString)(const T&) = constToString) { return optional ? toString(*optional) : "<not set>"; } diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h index 451918bb46..28e4816afe 100644 --- a/include/input/PropertyMap.h +++ b/include/input/PropertyMap.h @@ -14,14 +14,11 @@ * limitations under the License. */ -#ifndef _UTILS_PROPERTY_MAP_H -#define _UTILS_PROPERTY_MAP_H +#pragma once #include <android-base/result.h> -#include <utils/Errors.h> -#include <utils/KeyedVector.h> -#include <utils/String8.h> #include <utils/Tokenizer.h> +#include <unordered_map> namespace android { @@ -58,30 +55,27 @@ public: /* Adds a property. * Replaces the property with the same key if it is already present. */ - void addProperty(const String8& key, const String8& value); - - /* Returns true if the property map contains the specified key. */ - bool hasProperty(const String8& key) const; + void addProperty(const std::string& key, const std::string& value); /* Gets the value of a property and parses it. * Returns true and sets outValue if the key was found and its value was parsed successfully. * Otherwise returns false and does not modify outValue. (Also logs a warning.) */ - bool tryGetProperty(const String8& key, String8& outValue) const; - bool tryGetProperty(const String8& key, bool& outValue) const; - bool tryGetProperty(const String8& key, int32_t& outValue) const; - bool tryGetProperty(const String8& key, float& outValue) const; + bool tryGetProperty(const std::string& key, std::string& outValue) const; + bool tryGetProperty(const std::string& key, bool& outValue) const; + bool tryGetProperty(const std::string& key, int32_t& outValue) const; + bool tryGetProperty(const std::string& key, float& outValue) const; /* Adds all values from the specified property map. */ void addAll(const PropertyMap* map); - /* Gets the underlying property map. */ - inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; } - /* Loads a property map from a file. */ static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename); private: + /* Returns true if the property map contains the specified key. */ + bool hasProperty(const std::string& key) const; + class Parser { PropertyMap* mMap; Tokenizer* mTokenizer; @@ -95,13 +89,11 @@ private: status_t parseType(); status_t parseKey(); status_t parseKeyProperty(); - status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseModifier(const std::string& token, int32_t* outMetaState); status_t parseCharacterLiteral(char16_t* outCharacter); }; - KeyedVector<String8, String8> mProperties; + std::unordered_map<std::string, std::string> mProperties; }; } // namespace android - -#endif // _UTILS_PROPERTY_MAP_H diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h index eda628e233..1e4f6e7a4c 100644 --- a/include/input/TouchVideoFrame.h +++ b/include/input/TouchVideoFrame.h @@ -14,8 +14,9 @@ * limitations under the License. */ -#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H -#define _LIBINPUT_TOUCHVIDEOFRAME_H +#pragma once + +#include <ui/Rotation.h> #include <stdint.h> #include <sys/time.h> @@ -59,7 +60,7 @@ public: * Rotate the video frame. * The rotation value is an enum from ui/Rotation.h */ - void rotate(int32_t orientation); + void rotate(ui::Rotation orientation); private: uint32_t mHeight; @@ -75,5 +76,3 @@ private: }; } // namespace android - -#endif // _LIBINPUT_TOUCHVIDEOFRAME_H diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index 1acc2aef70..f3c201e7c4 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -14,13 +14,15 @@ * limitations under the License. */ -#ifndef _LIBINPUT_VELOCITY_CONTROL_H -#define _LIBINPUT_VELOCITY_CONTROL_H +#pragma once +#include <android-base/stringprintf.h> #include <input/Input.h> #include <input/VelocityTracker.h> #include <utils/Timers.h> +using android::base::StringPrintf; + namespace android { /* @@ -70,6 +72,12 @@ struct VelocityControlParameters { scale(scale), lowThreshold(lowThreshold), highThreshold(highThreshold), acceleration(acceleration) { } + + std::string dump() const { + return StringPrintf("scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + scale, lowThreshold, highThreshold, acceleration); + } }; /* @@ -79,6 +87,9 @@ class VelocityControl { public: VelocityControl(); + /* Gets the various parameters. */ + VelocityControlParameters& getParameters(); + /* Sets the various parameters. */ void setParameters(const VelocityControlParameters& parameters); @@ -98,10 +109,8 @@ private: VelocityControlParameters mParameters; nsecs_t mLastMovementTime; - VelocityTracker::Position mRawPosition; + float mRawPositionX, mRawPositionY; VelocityTracker mVelocityTracker; }; } // namespace android - -#endif // _LIBINPUT_VELOCITY_CONTROL_H diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 886f1f7753..62c3ae15ce 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -14,12 +14,13 @@ * limitations under the License. */ -#ifndef _LIBINPUT_VELOCITY_TRACKER_H -#define _LIBINPUT_VELOCITY_TRACKER_H +#pragma once #include <input/Input.h> #include <utils/BitSet.h> #include <utils/Timers.h> +#include <map> +#include <set> namespace android { @@ -46,18 +47,14 @@ public: MAX = LEGACY, }; - struct Position { - float x, y; - }; - struct Estimator { static const size_t MAX_DEGREE = 4; // Estimator time base. nsecs_t time; - // Polynomial coefficients describing motion in X and Y. - float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; + // Polynomial coefficients describing motion. + float coeff[MAX_DEGREE + 1]; // Polynomial degree (number of coefficients), or zero if no information is // available. @@ -71,18 +68,47 @@ public: degree = 0; confidence = 0; for (size_t i = 0; i <= MAX_DEGREE; i++) { - xCoeff[i] = 0; - yCoeff[i] = 0; + coeff[i] = 0; + } + } + }; + + /* + * Contains all available velocity data from a VelocityTracker. + */ + struct ComputedVelocity { + inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const { + const auto& axisVelocities = mVelocities.find(axis); + if (axisVelocities == mVelocities.end()) { + return {}; } + + const auto& axisIdVelocity = axisVelocities->second.find(id); + if (axisIdVelocity == axisVelocities->second.end()) { + return {}; + } + + return axisIdVelocity->second; } + + inline void addVelocity(int32_t axis, uint32_t id, float velocity) { + mVelocities[axis][id] = velocity; + } + + private: + std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities; }; - // Creates a velocity tracker using the specified strategy. + // Creates a velocity tracker using the specified strategy for each supported axis. // If strategy is not provided, uses the default strategy for the platform. + // TODO(b/32830165): support axis-specific strategies. VelocityTracker(const Strategy strategy = Strategy::DEFAULT); ~VelocityTracker(); + /** Return true if the axis is supported for velocity tracking, false otherwise. */ + static bool isAxisSupported(int32_t axis); + // Resets the velocity tracker state. void clear(); @@ -92,47 +118,57 @@ public: void clearPointers(BitSet32 idBits); // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose positions + // The idBits bitfield specifies the pointer ids of the pointers whose data points // are included in the movement. - // The positions array contains position information for each pointer in order by - // increasing id. Its size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions); + // The positions map contains a mapping of an axis to positions array. + // The positions arrays contain information for each pointer in order by increasing id. + // Each array's size should be equal to the number of one bits in idBits. + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::map<int32_t, std::vector<float>>& positions); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); - // Gets the velocity of the specified pointer id in position units per second. - // Returns false and sets the velocity components to zero if there is - // insufficient movement information for the pointer. - bool getVelocity(uint32_t id, float* outVx, float* outVy) const; + // Returns the velocity of the specified pointer id and axis in position units per second. + // Returns empty optional if there is insufficient movement information for the pointer, or if + // the given axis is not supported for velocity tracking. + std::optional<float> getVelocity(int32_t axis, uint32_t id) const; + + // Returns a ComputedVelocity instance with all available velocity data, using the given units + // (reference: units == 1 means "per millisecond"), and clamping each velocity between + // [-maxVelocity, maxVelocity], inclusive. + ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity); - // Gets an estimator for the recent movements of the specified pointer id. + // Gets an estimator for the recent movements of the specified pointer id for the given axis. // Returns false and clears the estimator if there is no information available // about the pointer. - bool getEstimator(uint32_t id, Estimator* outEstimator) const; + bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const; // Gets the active pointer id, or -1 if none. inline int32_t getActivePointerId() const { return mActivePointerId; } - // Gets a bitset containing all pointer ids from the most recent movement. - inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } - private: - // The default velocity tracker strategy. - // Although other strategies are available for testing and comparison purposes, - // this is the strategy that applications will actually use. Be very careful - // when adjusting the default strategy because it can dramatically affect - // (often in a bad way) the user experience. - static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2; - nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; int32_t mActivePointerId; - std::unique_ptr<VelocityTrackerStrategy> mStrategy; - - bool configureStrategy(const Strategy strategy); - static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy); + // An override strategy passed in the constructor to be used for all axes. + // This strategy will apply to all axes, unless the default strategy is specified here. + // When default strategy is specified, then each axis will use a potentially different strategy + // based on a hardcoded mapping. + const Strategy mOverrideStrategy; + // Maps axes to their respective VelocityTrackerStrategy instances. + // Note that, only axes that have had MotionEvents (and not all supported axes) will be here. + std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mConfiguredStrategies; + + void configureStrategy(int32_t axis); + + // Generates a VelocityTrackerStrategy instance for the given Strategy type. + // The `deltaValues` parameter indicates whether or not the created strategy should treat motion + // values as deltas (and not as absolute values). This the parameter is applicable only for + // strategies that support differential axes. + static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy, + bool deltaValues); }; @@ -146,10 +182,9 @@ protected: public: virtual ~VelocityTrackerStrategy() { } - virtual void clear() = 0; virtual void clearPointers(BitSet32 idBits) = 0; virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) = 0; + const std::vector<float>& positions) = 0; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; }; @@ -178,10 +213,9 @@ public: LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); virtual ~LeastSquaresVelocityTrackerStrategy(); - virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -196,11 +230,9 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; float chooseWeight(uint32_t index) const; @@ -221,10 +253,9 @@ public: IntegratingVelocityTrackerStrategy(uint32_t degree); ~IntegratingVelocityTrackerStrategy(); - virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -233,16 +264,15 @@ private: nsecs_t updateTime; uint32_t degree; - float xpos, xvel, xaccel; - float ypos, yvel, yaccel; + float pos, vel, accel; }; const uint32_t mDegree; BitSet32 mPointerIdBits; State mPointerState[MAX_POINTER_ID + 1]; - void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const; - void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void initState(State& state, nsecs_t eventTime, float pos) const; + void updateState(State& state, nsecs_t eventTime, float pos) const; void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; @@ -255,10 +285,9 @@ public: LegacyVelocityTrackerStrategy(); virtual ~LegacyVelocityTrackerStrategy(); - virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -274,11 +303,9 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; uint32_t mIndex; @@ -287,13 +314,12 @@ private: class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { public: - ImpulseVelocityTrackerStrategy(); + ImpulseVelocityTrackerStrategy(bool deltaValues); virtual ~ImpulseVelocityTrackerStrategy(); - virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -308,17 +334,18 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; + // Whether or not the input movement values for the strategy come in the form of delta values. + // If the input values are not deltas, the strategy needs to calculate deltas as part of its + // velocity calculation. + const bool mDeltaValues; + size_t mIndex; Movement mMovements[HISTORY_SIZE]; }; } // namespace android - -#endif // _LIBINPUT_VELOCITY_TRACKER_H diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h index 6e8e2c9cf4..a4381eaab9 100644 --- a/include/input/VirtualKeyMap.h +++ b/include/input/VirtualKeyMap.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _LIBINPUT_VIRTUAL_KEY_MAP_H -#define _LIBINPUT_VIRTUAL_KEY_MAP_H +#pragma once #include <stdint.h> @@ -77,5 +76,3 @@ private: }; } // namespace android - -#endif // _LIBINPUT_KEY_CHARACTER_MAP_H diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h index ed6f6f35f5..e0384f31db 100644 --- a/include/powermanager/PowerHalLoader.h +++ b/include/powermanager/PowerHalLoader.h @@ -19,6 +19,8 @@ #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/1.2/IPower.h> +#include <android/hardware/power/1.3/IPower.h> #include <android/hardware/power/IPower.h> namespace android { @@ -32,12 +34,16 @@ public: static sp<hardware::power::IPower> loadAidl(); static sp<hardware::power::V1_0::IPower> loadHidlV1_0(); static sp<hardware::power::V1_1::IPower> loadHidlV1_1(); + static sp<hardware::power::V1_2::IPower> loadHidlV1_2(); + static sp<hardware::power::V1_3::IPower> loadHidlV1_3(); private: static std::mutex gHalMutex; static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex); static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex); static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_3::IPower> gHalHidlV1_3 GUARDED_BY(gHalMutex); static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked() EXCLUSIVE_LOCKS_REQUIRED(gHalMutex); diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index dfb0ff59a0..8028aa86e1 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -19,6 +19,8 @@ #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/1.2/IPower.h> +#include <android/hardware/power/1.3/IPower.h> #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/IPowerHintSession.h> @@ -142,8 +144,8 @@ public: // Wrapper for the HIDL Power HAL v1.0. class HidlHalWrapperV1_0 : public HalWrapper { public: - explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal) - : mHandleV1_0(std::move(Hal)) {} + explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0) + : mHandleV1_0(std::move(handleV1_0)) {} virtual ~HidlHalWrapperV1_0() = default; virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; @@ -154,10 +156,10 @@ public: virtual HalResult<int64_t> getHintSessionPreferredRate() override; protected: - virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data); + const sp<hardware::power::V1_0::IPower> mHandleV1_0; + virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data); private: - sp<hardware::power::V1_0::IPower> mHandleV1_0; HalResult<void> setInteractive(bool enabled); HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled); }; @@ -165,17 +167,40 @@ private: // Wrapper for the HIDL Power HAL v1.1. class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { public: - HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0, - sp<hardware::power::V1_1::IPower> handleV1_1) - : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {} + HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1) + : HidlHalWrapperV1_0(std::move(handleV1_1)) {} virtual ~HidlHalWrapperV1_1() = default; protected: - virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, + virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; +}; -private: - sp<hardware::power::V1_1::IPower> mHandleV1_1; +// Wrapper for the HIDL Power HAL v1.2. +class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { +public: + virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; + HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2) + : HidlHalWrapperV1_1(std::move(handleV1_2)) {} + virtual ~HidlHalWrapperV1_2() = default; + +protected: + virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, + uint32_t data) override; +}; + +// Wrapper for the HIDL Power HAL v1.3. +class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { +public: + virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; + HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3) + : HidlHalWrapperV1_2(std::move(handleV1_3)) {} + virtual ~HidlHalWrapperV1_3() = default; + +protected: + virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, + uint32_t data) override; }; // Wrapper for the AIDL Power HAL. diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h index b36728e934..d52861add0 100644 --- a/libs/arect/include/android/rect.h +++ b/libs/arect/include/android/rect.h @@ -57,7 +57,7 @@ typedef struct ARect { } ARect; #ifdef __cplusplus -}; +} #endif #endif // ANDROID_RECT_H diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp index 2bf15d45eb..fddecc0ceb 100644 --- a/libs/attestation/Android.bp +++ b/libs/attestation/Android.bp @@ -22,6 +22,7 @@ package { cc_library_static { name: "libattestation", + host_supported: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 28369d6df0..f17bb7da14 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -321,10 +321,6 @@ cc_library { }, afdo: true, - - header_abi_checker: { - diff_flags: ["-allow-adding-removing-weak-symbols"], - }, } cc_library_static { @@ -463,9 +459,7 @@ aidl_interface { local_include_dir: "aidl", host_supported: true, srcs: [ - "aidl/android/content/pm/IPackageChangeObserver.aidl", "aidl/android/content/pm/IPackageManagerNative.aidl", - "aidl/android/content/pm/PackageChangeEvent.aidl", "aidl/android/content/pm/IStagedApexObserver.aidl", "aidl/android/content/pm/ApexStagedEvent.aidl", "aidl/android/content/pm/StagedApexInfo.aidl", diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index 7c99f76ec6..f8a8843309 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -17,7 +17,6 @@ package android.content.pm; -import android.content.pm.IPackageChangeObserver; import android.content.pm.IStagedApexObserver; import android.content.pm.StagedApexInfo; @@ -92,18 +91,6 @@ interface IPackageManagerNative { */ @utf8InCpp String getModuleMetadataPackageName(); - /* Returns the names of all packages. */ - @utf8InCpp String[] getAllPackages(); - - /** Register an extra package change observer to receive the multi-cast. */ - void registerPackageChangeObserver(in IPackageChangeObserver observer); - - /** - * Unregister an existing package change observer. - * This does nothing if this observer was not already registered. - */ - void unregisterPackageChangeObserver(in IPackageChangeObserver observer); - /** * Returns true if the package has the SHA 256 version of the signing certificate. * @see PackageManager#hasSigningCertificate(String, byte[], int), where type diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 9f7e2c87b8..8cc8105ff9 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -219,80 +219,78 @@ inline IBinder* BpInterface<INTERFACE>::onAsBinder() namespace internal { constexpr const char* const kManualInterfaces[] = { - "android.app.IActivityManager", - "android.app.IUidObserver", - "android.drm.IDrm", - "android.dvr.IVsyncCallback", - "android.dvr.IVsyncService", - "android.gfx.tests.ICallback", - "android.gfx.tests.IIPCTest", - "android.gfx.tests.ISafeInterfaceTest", - "android.graphicsenv.IGpuService", - "android.gui.IConsumerListener", - "android.gui.IGraphicBufferConsumer", - "android.gui.ITransactionComposerListener", - "android.gui.SensorEventConnection", - "android.gui.SensorServer", - "android.hardware.ICamera", - "android.hardware.ICameraClient", - "android.hardware.ICameraRecordingProxy", - "android.hardware.ICameraRecordingProxyListener", - "android.hardware.ICrypto", - "android.hardware.IOMXObserver", - "android.hardware.IStreamListener", - "android.hardware.IStreamSource", - "android.media.IAudioService", - "android.media.IDataSource", - "android.media.IDrmClient", - "android.media.IMediaCodecList", - "android.media.IMediaDrmService", - "android.media.IMediaExtractor", - "android.media.IMediaExtractorService", - "android.media.IMediaHTTPConnection", - "android.media.IMediaHTTPService", - "android.media.IMediaLogService", - "android.media.IMediaMetadataRetriever", - "android.media.IMediaMetricsService", - "android.media.IMediaPlayer", - "android.media.IMediaPlayerClient", - "android.media.IMediaPlayerService", - "android.media.IMediaRecorder", - "android.media.IMediaRecorderClient", - "android.media.IMediaResourceMonitor", - "android.media.IMediaSource", - "android.media.IRemoteDisplay", - "android.media.IRemoteDisplayClient", - "android.media.IResourceManagerClient", - "android.media.IResourceManagerService", - "android.os.IComplexTypeInterface", - "android.os.IPermissionController", - "android.os.IPingResponder", - "android.os.IProcessInfoService", - "android.os.ISchedulingPolicyService", - "android.os.IStringConstants", - "android.os.storage.IObbActionListener", - "android.os.storage.IStorageEventListener", - "android.os.storage.IStorageManager", - "android.os.storage.IStorageShutdownObserver", - "android.service.vr.IPersistentVrStateCallbacks", - "android.service.vr.IVrManager", - "android.service.vr.IVrStateCallbacks", - "android.ui.ISurfaceComposer", - "android.ui.ISurfaceComposerClient", - "android.utils.IMemory", - "android.utils.IMemoryHeap", - "com.android.car.procfsinspector.IProcfsInspector", - "com.android.internal.app.IAppOpsCallback", - "com.android.internal.app.IAppOpsService", - "com.android.internal.app.IBatteryStats", - "com.android.internal.os.IResultReceiver", - "com.android.internal.os.IShellCallback", - "drm.IDrmManagerService", - "drm.IDrmServiceListener", - "IAAudioClient", - "IAAudioService", - "VtsFuzzer", - nullptr, + "android.app.IActivityManager", + "android.app.IUidObserver", + "android.drm.IDrm", + "android.dvr.IVsyncCallback", + "android.dvr.IVsyncService", + "android.gfx.tests.ICallback", + "android.gfx.tests.IIPCTest", + "android.gfx.tests.ISafeInterfaceTest", + "android.graphicsenv.IGpuService", + "android.gui.IConsumerListener", + "android.gui.IGraphicBufferConsumer", + "android.gui.SensorEventConnection", + "android.gui.SensorServer", + "android.hardware.ICamera", + "android.hardware.ICameraClient", + "android.hardware.ICameraRecordingProxy", + "android.hardware.ICameraRecordingProxyListener", + "android.hardware.ICrypto", + "android.hardware.IOMXObserver", + "android.hardware.IStreamListener", + "android.hardware.IStreamSource", + "android.media.IAudioService", + "android.media.IDataSource", + "android.media.IDrmClient", + "android.media.IMediaCodecList", + "android.media.IMediaDrmService", + "android.media.IMediaExtractor", + "android.media.IMediaExtractorService", + "android.media.IMediaHTTPConnection", + "android.media.IMediaHTTPService", + "android.media.IMediaLogService", + "android.media.IMediaMetadataRetriever", + "android.media.IMediaMetricsService", + "android.media.IMediaPlayer", + "android.media.IMediaPlayerClient", + "android.media.IMediaPlayerService", + "android.media.IMediaRecorder", + "android.media.IMediaRecorderClient", + "android.media.IMediaResourceMonitor", + "android.media.IMediaSource", + "android.media.IRemoteDisplay", + "android.media.IRemoteDisplayClient", + "android.media.IResourceManagerClient", + "android.media.IResourceManagerService", + "android.os.IComplexTypeInterface", + "android.os.IPermissionController", + "android.os.IPingResponder", + "android.os.IProcessInfoService", + "android.os.ISchedulingPolicyService", + "android.os.IStringConstants", + "android.os.storage.IObbActionListener", + "android.os.storage.IStorageEventListener", + "android.os.storage.IStorageManager", + "android.os.storage.IStorageShutdownObserver", + "android.service.vr.IPersistentVrStateCallbacks", + "android.service.vr.IVrManager", + "android.service.vr.IVrStateCallbacks", + "android.ui.ISurfaceComposer", + "android.utils.IMemory", + "android.utils.IMemoryHeap", + "com.android.car.procfsinspector.IProcfsInspector", + "com.android.internal.app.IAppOpsCallback", + "com.android.internal.app.IAppOpsService", + "com.android.internal.app.IBatteryStats", + "com.android.internal.os.IResultReceiver", + "com.android.internal.os.IShellCallback", + "drm.IDrmManagerService", + "drm.IDrmServiceListener", + "IAAudioClient", + "IAAudioService", + "VtsFuzzer", + nullptr, }; constexpr const char* const kDownstreamManualInterfaces[] = { diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index c5d3a3207c..5f145a149d 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -22,6 +22,7 @@ cc_library_shared { double_loadable: true, srcs: [ + ":libgui_frame_event_aidl", "BufferQueueConverter.cpp", ], diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index c010a2e58a..8e57152b49 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -14,12 +14,18 @@ cc_test { address: true, }, srcs: [ + "algorithm_test.cpp", "cast_test.cpp", "concat_test.cpp", "enum_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", "future_test.cpp", + "match_test.cpp", + "mixins_test.cpp", + "non_null_test.cpp", + "optional_test.cpp", + "shared_mutex_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp new file mode 100644 index 0000000000..8052caf642 --- /dev/null +++ b/libs/ftl/algorithm_test.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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 <ftl/algorithm.h> +#include <ftl/small_map.h> +#include <ftl/static_vector.h> +#include <gtest/gtest.h> + +#include <string_view> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(Algorithm, FindIf) { + using namespace std::string_view_literals; + + const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; + EXPECT_EQ(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }), "cake"sv); + + const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>( + 12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv); + + using Map = decltype(map); + + EXPECT_EQ(14, ftl::find_if(map, [](const auto& pair) { + return pair.second.size() == 3; + }).transform(ftl::to_key<Map>)); + + const auto opt = ftl::find_if(map, [](const auto& pair) { + return pair.second.size() == 1; + }).transform(ftl::to_mapped_ref<Map>); + + ASSERT_TRUE(opt); + EXPECT_EQ(opt->get(), ftl::StaticVector("tiramisu"sv)); +} + +} // namespace android::test diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp index 8ecb1b252d..771f05478a 100644 --- a/libs/ftl/concat_test.cpp +++ b/libs/ftl/concat_test.cpp @@ -28,8 +28,25 @@ TEST(Concat, Example) { EXPECT_EQ(string.c_str()[string.size()], '\0'); } +TEST(Concat, Characters) { + EXPECT_EQ(ftl::Concat(u'a', ' ', U'b').str(), "97 98"); +} + +TEST(Concat, References) { + int i[] = {-1, 2}; + unsigned u = 3; + EXPECT_EQ(ftl::Concat(i[0], std::as_const(i[1]), u).str(), "-123"); + + const bool b = false; + const char c = 'o'; + EXPECT_EQ(ftl::Concat(b, "tt", c).str(), "falsetto"); +} + namespace { +static_assert(ftl::Concat{true, false, true}.str() == "truefalsetrue"); +static_assert(ftl::Concat{':', '-', ')'}.str() == ":-)"); + static_assert(ftl::Concat{"foo"}.str() == "foo"); static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo"); diff --git a/libs/ftl/match_test.cpp b/libs/ftl/match_test.cpp new file mode 100644 index 0000000000..a6cff2eed6 --- /dev/null +++ b/libs/ftl/match_test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2022 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 <ftl/match.h> +#include <gtest/gtest.h> + +#include <chrono> +#include <string> +#include <variant> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(Match, Example) { + using namespace std::chrono; + using namespace std::chrono_literals; + using namespace std::string_literals; + + std::variant<seconds, minutes, hours> duration = 119min; + + // Mutable match. + ftl::match(duration, [](auto& d) { ++d; }); + + // Immutable match. Exhaustive due to minutes being convertible to seconds. + EXPECT_EQ("2 hours"s, + ftl::match( + duration, + [](const seconds& s) { + const auto h = duration_cast<hours>(s); + return std::to_string(h.count()) + " hours"s; + }, + [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; })); +} + +} // namespace android::test diff --git a/libs/ftl/mixins_test.cpp b/libs/ftl/mixins_test.cpp new file mode 100644 index 0000000000..2c9f9dfd8a --- /dev/null +++ b/libs/ftl/mixins_test.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2022 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 <ftl/mixins.h> +#include <gtest/gtest.h> + +#include <chrono> +#include <functional> +#include <type_traits> +#include <utility> + +namespace android::test { +namespace { + +// Keep in sync with example usage in header file. + +struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> { + using Constructible::Constructible; +}; + +static_assert(!std::is_default_constructible_v<Id>); + +struct Color : ftl::DefaultConstructible<Color, std::uint8_t>, + ftl::Equatable<Color>, + ftl::Orderable<Color> { + using DefaultConstructible::DefaultConstructible; +}; + +static_assert(Color() == Color(0u)); +static_assert(ftl::to_underlying(Color(-1)) == 255u); +static_assert(Color(1u) < Color(2u)); + +struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>, + ftl::Equatable<Sequence>, + ftl::Orderable<Sequence>, + ftl::Incrementable<Sequence> { + using DefaultConstructible::DefaultConstructible; +}; + +static_assert(Sequence() == Sequence(-1)); + +struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>, + ftl::Equatable<Timeout>, + ftl::Addable<Timeout> { + using DefaultConstructible::DefaultConstructible; +}; + +using namespace std::chrono_literals; +static_assert(Timeout() + Timeout(5s) == Timeout(15s)); + +// Construction. +constexpr Id kId{1234}; +constexpr Sequence kSequence; + +// Underlying value. +static_assert(ftl::to_underlying(Id(-42)) == -42); +static_assert(ftl::to_underlying(kSequence) == -1); + +// Casting. +static_assert(static_cast<std::int32_t>(Id(-1)) == -1); +static_assert(static_cast<std::int8_t>(kSequence) == -1); + +static_assert(!std::is_convertible_v<std::int32_t, Id>); +static_assert(!std::is_convertible_v<Id, std::int32_t>); + +// Equality. +static_assert(kId == Id(1234)); +static_assert(kId != Id(123)); +static_assert(kSequence == Sequence(-1)); + +// Ordering. +static_assert(Sequence(1) < Sequence(2)); +static_assert(Sequence(2) > Sequence(1)); +static_assert(Sequence(3) <= Sequence(4)); +static_assert(Sequence(4) >= Sequence(3)); +static_assert(Sequence(5) <= Sequence(5)); +static_assert(Sequence(6) >= Sequence(6)); + +// Incrementing. +template <typename Op, typename T, typename... Ts> +constexpr auto mutable_op(Op op, T lhs, Ts... rhs) { + const T result = op(lhs, rhs...); + return std::make_pair(lhs, result); +} + +static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Sequence()) == + std::make_pair(Sequence(0), Sequence(0))); + +static_assert(mutable_op([](auto& lhs) { return lhs++; }, Sequence()) == + std::make_pair(Sequence(0), Sequence(-1))); + +// Addition. + +// `Addable` implies `Incrementable`. +static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Timeout()) == + std::make_pair(Timeout(11s), Timeout(11s))); + +static_assert(mutable_op([](auto& lhs) { return lhs++; }, Timeout()) == + std::make_pair(Timeout(11s), Timeout(10s))); + +static_assert(Timeout(5s) + Timeout(6s) == Timeout(11s)); + +static_assert(mutable_op([](auto& lhs, const auto& rhs) { return lhs += rhs; }, Timeout(7s), + Timeout(8s)) == std::make_pair(Timeout(15s), Timeout(15s))); + +// Type safety. + +namespace traits { + +template <typename, typename = void> +struct is_incrementable : std::false_type {}; + +template <typename T> +struct is_incrementable<T, std::void_t<decltype(++std::declval<T&>())>> : std::true_type {}; + +template <typename T> +constexpr bool is_incrementable_v = is_incrementable<T>{}; + +template <typename, typename, typename, typename = void> +struct has_binary_op : std::false_type {}; + +template <typename Op, typename T, typename U> +struct has_binary_op<Op, T, U, std::void_t<decltype(Op{}(std::declval<T&>(), std::declval<U&>()))>> + : std::true_type {}; + +template <typename T, typename U> +constexpr bool is_equatable_v = + has_binary_op<std::equal_to<void>, T, U>{} && has_binary_op<std::not_equal_to<void>, T, U>{}; + +template <typename T, typename U> +constexpr bool is_orderable_v = + has_binary_op<std::less<void>, T, U>{} && has_binary_op<std::less_equal<void>, T, U>{} && + has_binary_op<std::greater<void>, T, U>{} && has_binary_op<std::greater_equal<void>, T, U>{}; + +template <typename T, typename U> +constexpr bool is_addable_v = has_binary_op<std::plus<void>, T, U>{}; + +} // namespace traits + +struct Real : ftl::Constructible<Real, float> { + using Constructible::Constructible; +}; + +static_assert(traits::is_equatable_v<Id, Id>); +static_assert(!traits::is_equatable_v<Real, Real>); +static_assert(!traits::is_equatable_v<Id, Color>); +static_assert(!traits::is_equatable_v<Sequence, Id>); +static_assert(!traits::is_equatable_v<Id, std::int32_t>); +static_assert(!traits::is_equatable_v<std::chrono::seconds, Timeout>); + +static_assert(traits::is_orderable_v<Color, Color>); +static_assert(!traits::is_orderable_v<Id, Id>); +static_assert(!traits::is_orderable_v<Real, Real>); +static_assert(!traits::is_orderable_v<Color, Sequence>); +static_assert(!traits::is_orderable_v<Color, std::uint8_t>); +static_assert(!traits::is_orderable_v<std::chrono::seconds, Timeout>); + +static_assert(traits::is_incrementable_v<Sequence>); +static_assert(traits::is_incrementable_v<Timeout>); +static_assert(!traits::is_incrementable_v<Id>); +static_assert(!traits::is_incrementable_v<Color>); +static_assert(!traits::is_incrementable_v<Real>); + +static_assert(traits::is_addable_v<Timeout, Timeout>); +static_assert(!traits::is_addable_v<Id, Id>); +static_assert(!traits::is_addable_v<Real, Real>); +static_assert(!traits::is_addable_v<Sequence, Sequence>); +static_assert(!traits::is_addable_v<Timeout, Sequence>); +static_assert(!traits::is_addable_v<Color, Timeout>); + +} // namespace +} // namespace android::test diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp new file mode 100644 index 0000000000..bd0462b3b6 --- /dev/null +++ b/libs/ftl/non_null_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2022 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 <ftl/non_null.h> +#include <gtest/gtest.h> + +#include <memory> +#include <string> +#include <string_view> + +namespace android::test { +namespace { + +void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr, + ftl::NonNull<std::size_t*> length_ptr) { + // No need for `nullptr` checks. + *length_ptr = string_ptr->length(); +} + +using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>; + +Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) { + // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point. + auto unique_ptr = std::move(non_null_ptr).take(); + + auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr))); + auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr; + + return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)}; +} + +} // namespace + +// Keep in sync with example usage in header file. +TEST(NonNull, Example) { + const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android")); + std::size_t size; + get_length(string_ptr, ftl::as_non_null(&size)); + EXPECT_EQ(size, 7u); + + auto ptr = ftl::as_non_null(std::make_unique<int>(42)); + const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true); + EXPECT_EQ(ptr1.get(), ptr2); +} + +namespace { + +constexpr std::string_view kApple = "apple"; +constexpr std::string_view kOrange = "orange"; + +using StringViewPtr = ftl::NonNull<const std::string_view*>; +constexpr StringViewPtr kApplePtr = ftl::as_non_null(&kApple); +constexpr StringViewPtr kOrangePtr = ftl::as_non_null(&kOrange); + +constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) { + return ptr1->length() > ptr2->length() ? ptr1 : ptr2; +} + +static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr); + +} // namespace +} // namespace android::test diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp new file mode 100644 index 0000000000..6b3b6c49e5 --- /dev/null +++ b/libs/ftl/optional_test.cpp @@ -0,0 +1,198 @@ +/* + * Copyright 2022 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 <ftl/optional.h> +#include <ftl/static_vector.h> +#include <ftl/string.h> +#include <ftl/unit.h> +#include <gtest/gtest.h> + +#include <cstdlib> +#include <functional> +#include <numeric> +#include <utility> + +using namespace std::placeholders; +using namespace std::string_literals; + +namespace android::test { + +using ftl::Optional; +using ftl::StaticVector; + +TEST(Optional, Construct) { + // Empty. + EXPECT_EQ(std::nullopt, Optional<int>()); + EXPECT_EQ(std::nullopt, Optional<std::string>(std::nullopt)); + + // Value. + EXPECT_EQ('?', Optional('?')); + EXPECT_EQ(""s, Optional(std::string())); + + // In place. + EXPECT_EQ("???"s, Optional<std::string>(std::in_place, 3u, '?')); + EXPECT_EQ("abc"s, Optional<std::string>(std::in_place, {'a', 'b', 'c'})); + + // Implicit downcast. + { + Optional opt = std::optional("test"s); + static_assert(std::is_same_v<decltype(opt), Optional<std::string>>); + + ASSERT_TRUE(opt); + EXPECT_EQ(opt.value(), "test"s); + } +} + +TEST(Optional, Transform) { + // Empty. + EXPECT_EQ(std::nullopt, Optional<int>().transform([](int) { return 0; })); + + // By value. + EXPECT_EQ(0, Optional(0).transform([](int x) { return x; })); + EXPECT_EQ(100, Optional(99).transform([](int x) { return x + 1; })); + EXPECT_EQ("0b100"s, Optional(4).transform(std::bind(ftl::to_string<int>, _1, ftl::Radix::kBin))); + + // By reference. + { + Optional opt = 'x'; + EXPECT_EQ('z', opt.transform([](char& c) { + c = 'y'; + return 'z'; + })); + + EXPECT_EQ('y', opt); + } + + // By rvalue reference. + { + std::string out; + EXPECT_EQ("xyz"s, Optional("abc"s).transform([&out](std::string&& str) { + out = std::move(str); + return "xyz"s; + })); + + EXPECT_EQ(out, "abc"s); + } + + // No return value. + { + Optional opt = "food"s; + EXPECT_EQ(ftl::unit, opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); }))); + EXPECT_EQ(opt, "foo"s); + } + + // Chaining. + EXPECT_EQ(14u, Optional(StaticVector{"upside"s, "down"s}) + .transform([](StaticVector<std::string, 3>&& v) { + v.push_back("cake"s); + return v; + }) + .transform([](const StaticVector<std::string, 3>& v) { + return std::accumulate(v.begin(), v.end(), std::string()); + }) + .transform([](const std::string& s) { return s.length(); })); +} + +namespace { + +Optional<int> parse_int(const std::string& str) { + if (const int i = std::atoi(str.c_str())) return i; + return std::nullopt; +} + +} // namespace + +TEST(Optional, AndThen) { + // Empty. + EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) -> Optional<int> { return 0; })); + EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) { return Optional<int>(); })); + + // By value. + EXPECT_EQ(0, Optional(0).and_then([](int x) { return Optional(x); })); + EXPECT_EQ(123, Optional("123").and_then(parse_int)); + EXPECT_EQ(std::nullopt, Optional("abc").and_then(parse_int)); + + // By reference. + { + Optional opt = 'x'; + EXPECT_EQ('z', opt.and_then([](char& c) { + c = 'y'; + return Optional('z'); + })); + + EXPECT_EQ('y', opt); + } + + // By rvalue reference. + { + std::string out; + EXPECT_EQ("xyz"s, Optional("abc"s).and_then([&out](std::string&& str) { + out = std::move(str); + return Optional("xyz"s); + })); + + EXPECT_EQ(out, "abc"s); + } + + // Chaining. + using StringVector = StaticVector<std::string, 3>; + EXPECT_EQ(14u, Optional(StaticVector{"-"s, "1"s}) + .and_then([](StringVector&& v) -> Optional<StringVector> { + if (v.push_back("4"s)) return v; + return {}; + }) + .and_then([](const StringVector& v) -> Optional<std::string> { + if (v.full()) return std::accumulate(v.begin(), v.end(), std::string()); + return {}; + }) + .and_then(parse_int) + .and_then([](int i) { + return i > 0 ? std::nullopt : std::make_optional(static_cast<unsigned>(-i)); + })); +} + +// Comparison. +namespace { + +constexpr Optional<int> kOptional1 = 1; +constexpr Optional<int> kAnotherOptional1 = 1; +constexpr Optional<int> kOptional2 = 2; +constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty; + +constexpr std::optional<int> kStdOptional1 = 1; + +static_assert(kOptional1 == kAnotherOptional1); + +static_assert(kOptional1 != kOptional2); +static_assert(kOptional2 != kOptional1); + +static_assert(kOptional1 != kOptionalEmpty); +static_assert(kOptionalEmpty != kOptional1); + +static_assert(kOptionalEmpty == kAnotherOptionalEmpty); + +static_assert(kOptional1 == kStdOptional1); +static_assert(kStdOptional1 == kOptional1); + +static_assert(kOptional2 != kStdOptional1); +static_assert(kStdOptional1 != kOptional2); + +static_assert(kOptional2 != kOptionalEmpty); +static_assert(kOptionalEmpty != kOptional2); + +} // namespace + +} // namespace android::test diff --git a/libs/ftl/shared_mutex_test.cpp b/libs/ftl/shared_mutex_test.cpp new file mode 100644 index 0000000000..6da7061ae0 --- /dev/null +++ b/libs/ftl/shared_mutex_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2022 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 <ftl/shared_mutex.h> +#include <gtest/gtest.h> +#include <ftl/fake_guard.h> + +namespace android::test { + +TEST(SharedMutex, SharedLock) { + ftl::SharedMutex mutex; + std::shared_lock shared_lock(mutex); + + { std::shared_lock shared_lock2(mutex); } +} + +TEST(SharedMutex, ExclusiveLock) { + ftl::SharedMutex mutex; + std::unique_lock unique_lock(mutex); +} + +TEST(SharedMutex, Annotations) { + struct { + void foo() FTL_ATTRIBUTE(requires_shared_capability(mutex)) { num++; } + void bar() FTL_ATTRIBUTE(requires_capability(mutex)) { num++; } + void baz() { + std::shared_lock shared_lock(mutex); + num++; + } + ftl::SharedMutex mutex; + int num = 0; + + } s; + + { + // TODO(b/257958323): Use an RAII class instead of locking manually. + s.mutex.lock_shared(); + s.foo(); + s.baz(); + s.mutex.unlock_shared(); + } + s.mutex.lock(); + s.bar(); + s.mutex.unlock(); +} + +} // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 1740a2b54c..634877f672 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -15,12 +15,15 @@ */ #include <ftl/small_map.h> +#include <ftl/unit.h> #include <gtest/gtest.h> #include <cctype> #include <string> +#include <string_view> using namespace std::string_literals; +using namespace std::string_view_literals; namespace android::test { @@ -38,7 +41,7 @@ TEST(SmallMap, Example) { EXPECT_TRUE(map.contains(123)); - EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u); + EXPECT_EQ(map.get(42).transform([](const std::string& s) { return s.size(); }), 3u); const auto opt = map.get(-1); ASSERT_TRUE(opt); @@ -50,7 +53,7 @@ TEST(SmallMap, Example) { map.emplace_or_replace(0, "vanilla", 2u, 3u); EXPECT_TRUE(map.dynamic()); - EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv))); } TEST(SmallMap, Construct) { @@ -70,7 +73,7 @@ TEST(SmallMap, Construct) { EXPECT_EQ(map.max_size(), 5u); EXPECT_FALSE(map.dynamic()); - EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi"))); + EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc"sv)(456, "def"sv)(789, "ghi"sv))); } { // In-place constructor with different types. @@ -81,7 +84,7 @@ TEST(SmallMap, Construct) { EXPECT_EQ(map.max_size(), 5u); EXPECT_FALSE(map.dynamic()); - EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0"))); + EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???"sv)(123, "abc"sv)(-1, ""sv))); } { // In-place constructor with implicit size. @@ -92,7 +95,7 @@ TEST(SmallMap, Construct) { EXPECT_EQ(map.max_size(), 3u); EXPECT_FALSE(map.dynamic()); - EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc"))); + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""sv)(42, "???"sv)(123, "abc"sv))); } } @@ -108,7 +111,7 @@ TEST(SmallMap, Assign) { { // Convertible types; same capacity. SmallMap map1 = ftl::init::map<char, std::string>('M', "mega")('G', "giga"); - const SmallMap map2 = ftl::init::map('T', "tera")('P', "peta"); + const SmallMap map2 = ftl::init::map('T', "tera"sv)('P', "peta"sv); map1 = map2; EXPECT_EQ(map1, map2); @@ -147,7 +150,7 @@ TEST(SmallMap, UniqueKeys) { } } -TEST(SmallMap, Find) { +TEST(SmallMap, Get) { { // Constant reference. const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); @@ -172,14 +175,15 @@ TEST(SmallMap, Find) { EXPECT_EQ(d, 'D'); } { - // Constant unary operation. + // Immutable transform operation. const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z'); + EXPECT_EQ(map.get('c').transform([](char c) { return std::toupper(c); }), 'Z'); } { - // Mutable unary operation. + // Mutable transform operation. SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); })); + EXPECT_EQ(map.get('c').transform(ftl::unit_fn([](char& c) { c = std::toupper(c); })), + ftl::unit); EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x'))); } @@ -247,7 +251,7 @@ TEST(SmallMap, TryReplace) { } { // Replacement arguments can refer to the replaced mapping. - const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; }); ASSERT_TRUE(ref); // Construct std::string from one character. @@ -292,7 +296,7 @@ TEST(SmallMap, EmplaceOrReplace) { } { // Replacement arguments can refer to the replaced mapping. - const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; }); ASSERT_TRUE(ref); // Construct std::string from one character. diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index f5af425fca..6d1dfe8124 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -23,6 +23,7 @@ package { cc_library { name: "libgralloctypes", + defaults: ["android.hardware.graphics.common-ndk_shared"], cflags: [ "-Wall", "-Werror", @@ -51,7 +52,6 @@ cc_library { ], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", "liblog", diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 2ac11746c3..a988e39de0 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -120,6 +120,12 @@ filegroup { path: "aidl/", } +filegroup { + name: "libgui_frame_event_aidl", + srcs: ["aidl/android/gui/FrameEvent.aidl"], + path: "aidl/", +} + cc_library_static { name: "libgui_aidl_static", vendor_available: true, @@ -136,16 +142,24 @@ cc_library_static { "include", ], + include_dirs: [ + "frameworks/native/include", + ], + export_shared_lib_headers: [ "libbinder", ], static_libs: [ "libui-types", + "libgui_window_info_static", ], aidl: { export_aidl_headers: true, + include_dirs: [ + "frameworks/native/libs/gui", + ], }, } @@ -178,19 +192,18 @@ cc_library_shared { "BitTube.cpp", "BLASTBufferQueue.cpp", "BufferItemConsumer.cpp", + "CompositorTiming.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", "DebugEGLImageTracker.cpp", "DisplayEventDispatcher.cpp", "DisplayEventReceiver.cpp", - "FrameTimelineInfo.cpp", "GLConsumer.cpp", "IConsumerListener.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "ISurfaceComposer.cpp", - "ISurfaceComposerClient.cpp", "ITransactionCompletedListener.cpp", "LayerDebugInfo.cpp", "LayerMetadata.cpp", @@ -202,7 +215,6 @@ cc_library_shared { "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", - "TransactionTracing.cpp", "VsyncEventData.cpp", "view/Surface.cpp", "WindowInfosListenerReporter.cpp", @@ -259,10 +271,16 @@ cc_library_static { defaults: ["libgui_bufferqueue-defaults"], srcs: [ + ":libgui_frame_event_aidl", ":inputconstants_aidl", ":libgui_bufferqueue_sources", - ":libgui_aidl", ], + + aidl: { + include_dirs: [ + "frameworks/native/libs/gui", + ], + }, } filegroup { @@ -293,6 +311,8 @@ filegroup { cc_defaults { name: "libgui_bufferqueue-defaults", + defaults: ["android.hardware.graphics.common-ndk_shared"], + cflags: [ "-Wall", "-Werror", @@ -321,7 +341,6 @@ cc_defaults { "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", "android.hardware.graphics.common@1.2", - "android.hardware.graphics.common-V4-ndk", "android.hidl.token@1.0-utils", "libbase", "libcutils", @@ -381,6 +400,7 @@ cc_library_static { ], srcs: [ + ":libgui_frame_event_aidl", "mock/GraphicBufferConsumer.cpp", "mock/GraphicBufferProducer.cpp", ], diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 24a5295112..97e45c6d47 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -33,6 +33,7 @@ #include <utils/Trace.h> #include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <chrono> @@ -158,23 +159,23 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati id++; mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); - mBufferItemConsumer->setBufferFreedListener(this); - ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); + ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; mNumAcquired = 0; mNumFrameAvailable = 0; TransactionCompletedListener::getInstance()->addQueueStallListener( - [&]() { - std::function<void(bool)> callbackCopy; - { - std::unique_lock _lock{mMutex}; - callbackCopy = mTransactionHangCallback; - } - if (callbackCopy) callbackCopy(true); - }, this); + [&](const std::string& reason) { + std::function<void(const std::string&)> callbackCopy; + { + std::unique_lock _lock{mMutex}; + callbackCopy = mTransactionHangCallback; + } + if (callbackCopy) callbackCopy(reason); + }, + this); BQA_LOGV("BLASTBufferQueue created"); } @@ -334,9 +335,11 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC); if (statsOptional) { SurfaceControlStats stat = *statsOptional; - mTransformHint = stat.transformHint; - mBufferItemConsumer->setTransformHint(mTransformHint); - BQA_LOGV("updated mTransformHint=%d", mTransformHint); + if (stat.transformHint) { + mTransformHint = *stat.transformHint; + mBufferItemConsumer->setTransformHint(mTransformHint); + BQA_LOGV("updated mTransformHint=%d", mTransformHint); + } // Update frametime stamps if the frame was latched and presented, indicated by a // valid latch time. if (stat.latchTime > 0) { @@ -598,7 +601,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( if (dequeueTime != mDequeueTimestamps.end()) { Parcel p; p.writeInt64(dequeueTime->second); - t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p); + t->setMetadata(mSurfaceControl, gui::METADATA_DEQUEUE_TIME, p); mDequeueTimestamps.erase(dequeueTime); } } @@ -631,6 +634,7 @@ Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { } void BLASTBufferQueue::acquireAndReleaseBuffer() { + BBQ_TRACE(); BufferItem bufferItem; status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); @@ -644,6 +648,7 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { } void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { + BBQ_TRACE(); if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) { // We are waiting on a previous sync's transaction callback so allow another sync // transaction to proceed. @@ -674,8 +679,8 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { bool waitForTransactionCallback = !mSyncedFrameNumbers.empty(); { - BBQ_TRACE(); std::unique_lock _lock{mMutex}; + BBQ_TRACE(); const bool syncTransactionSet = mTransactionReadyCallback != nullptr; BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); @@ -1113,57 +1118,13 @@ uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { return mLastAcquiredFrameNumber; } -void BLASTBufferQueue::abandon() { - std::unique_lock _lock{mMutex}; - // flush out the shadow queue - while (mNumFrameAvailable > 0) { - acquireAndReleaseBuffer(); - } - - // Clear submitted buffer states - mNumAcquired = 0; - mSubmitted.clear(); - mPendingRelease.clear(); - - if (!mPendingTransactions.empty()) { - BQA_LOGD("Applying pending transactions on abandon %d", - static_cast<uint32_t>(mPendingTransactions.size())); - SurfaceComposerClient::Transaction t; - mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */); - // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction - t.setApplyToken(mApplyToken).apply(false, true); - } - - // Clear sync states - if (!mSyncedFrameNumbers.empty()) { - BQA_LOGD("mSyncedFrameNumbers cleared"); - mSyncedFrameNumbers.clear(); - } - - if (mSyncTransaction != nullptr) { - BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s", - mAcquireSingleBuffer ? "true" : "false"); - mSyncTransaction = nullptr; - mAcquireSingleBuffer = false; - } - - // abandon buffer queue - if (mBufferItemConsumer != nullptr) { - mBufferItemConsumer->abandon(); - mBufferItemConsumer->setFrameAvailableListener(nullptr); - mBufferItemConsumer->setBufferFreedListener(nullptr); - } - mBufferItemConsumer = nullptr; - mConsumer = nullptr; - mProducer = nullptr; -} - bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const { std::unique_lock _lock{mMutex}; return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl); } -void BLASTBufferQueue::setTransactionHangCallback(std::function<void(bool)> callback) { +void BLASTBufferQueue::setTransactionHangCallback( + std::function<void(const std::string&)> callback) { std::unique_lock _lock{mMutex}; mTransactionHangCallback = callback; } diff --git a/libs/gui/CompositorTiming.cpp b/libs/gui/CompositorTiming.cpp new file mode 100644 index 0000000000..50f7b252b6 --- /dev/null +++ b/libs/gui/CompositorTiming.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CompositorTiming" + +#include <cutils/compiler.h> +#include <gui/CompositorTiming.h> +#include <log/log.h> + +namespace android::gui { + +CompositorTiming::CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase, + nsecs_t presentLatency) { + if (CC_UNLIKELY(vsyncPeriod <= 0)) { + ALOGE("Invalid VSYNC period"); + return; + } + + const nsecs_t idealLatency = [=] { + // Modulo rounds toward 0 not INT64_MIN, so treat signs separately. + if (vsyncPhase < 0) return -vsyncPhase % vsyncPeriod; + + const nsecs_t latency = (vsyncPeriod - vsyncPhase) % vsyncPeriod; + return latency > 0 ? latency : vsyncPeriod; + }(); + + // Snap the latency to a value that removes scheduling jitter from the composite and present + // times, which often have >1ms of jitter. Reducing jitter is important if an app attempts to + // extrapolate something like user input to an accurate present time. Snapping also allows an + // app to precisely calculate vsyncPhase with (presentLatency % interval). + const nsecs_t bias = vsyncPeriod / 2; + const nsecs_t extraVsyncs = (presentLatency - idealLatency + bias) / vsyncPeriod; + const nsecs_t snappedLatency = + extraVsyncs > 0 ? idealLatency + extraVsyncs * vsyncPeriod : idealLatency; + + this->deadline = vsyncDeadline - idealLatency; + this->interval = vsyncPeriod; + this->presentLatency = snappedLatency; +} + +} // namespace android::gui diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index dfdce20438..501e69ade5 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -35,11 +35,14 @@ static const size_t EVENT_BUFFER_SIZE = 100; static constexpr nsecs_t WAITING_FOR_VSYNC_TIMEOUT = ms2ns(300); -DisplayEventDispatcher::DisplayEventDispatcher( - const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::EventRegistrationFlags eventRegistration) - : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false), - mLastVsyncCount(0), mLastScheduleVsyncTime(0) { +DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper, + gui::ISurfaceComposer::VsyncSource vsyncSource, + EventRegistrationFlags eventRegistration) + : mLooper(looper), + mReceiver(vsyncSource, eventRegistration), + mWaitingForVsync(false), + mLastVsyncCount(0), + mLastScheduleVsyncTime(0) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index bfb77699c0..c52fb6b7c3 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -19,10 +19,9 @@ #include <utils/Errors.h> #include <gui/DisplayEventReceiver.h> -#include <gui/ISurfaceComposer.h> #include <gui/VsyncEventData.h> -#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <private/gui/BitTube.h> @@ -32,15 +31,20 @@ namespace android { // --------------------------------------------------------------------------- -DisplayEventReceiver::DisplayEventReceiver( - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::EventRegistrationFlags eventRegistration) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); +DisplayEventReceiver::DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource vsyncSource, + EventRegistrationFlags eventRegistration) { + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); if (sf != nullptr) { - mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration); + mEventConnection = nullptr; + binder::Status status = + sf->createDisplayEventConnection(vsyncSource, + static_cast< + gui::ISurfaceComposer::EventRegistration>( + eventRegistration.get()), + &mEventConnection); if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); - const auto status = mEventConnection->stealReceiveChannel(mDataChannel.get()); + status = mEventConnection->stealReceiveChannel(mDataChannel.get()); if (!status.isOk()) { ALOGE("stealReceiveChannel failed: %s", status.toString8().c_str()); mInitError = std::make_optional<status_t>(status.transactionError()); diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp index 52d9540eeb..bd640df81e 100644 --- a/libs/gui/DisplayInfo.cpp +++ b/libs/gui/DisplayInfo.cpp @@ -20,8 +20,13 @@ #include <gui/DisplayInfo.h> #include <private/gui/ParcelUtils.h> +#include <android-base/stringprintf.h> #include <log/log.h> +#include <inttypes.h> + +#define INDENT " " + namespace android::gui { // --- DisplayInfo --- @@ -67,4 +72,17 @@ status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const { return OK; } +void DisplayInfo::dump(std::string& out, const char* prefix) const { + using android::base::StringAppendF; + + out += prefix; + StringAppendF(&out, "DisplayViewport[id=%" PRId32 "]\n", displayId); + out += prefix; + StringAppendF(&out, INDENT "Width=%" PRId32 ", Height=%" PRId32 "\n", logicalWidth, + logicalHeight); + std::string transformPrefix(prefix); + transformPrefix.append(INDENT); + transform.dump(out, "Transform", transformPrefix.c_str()); +} + } // namespace android::gui diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp deleted file mode 100644 index 3800b88ab0..0000000000 --- a/libs/gui/FrameTimelineInfo.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "FrameTimelineInfo" - -#include <inttypes.h> - -#include <android/os/IInputConstants.h> -#include <gui/FrameTimelineInfo.h> -#include <gui/LayerState.h> -#include <private/gui/ParcelUtils.h> -#include <utils/Errors.h> - -#include <cmath> - -using android::os::IInputConstants; - -namespace android { - -status_t FrameTimelineInfo::write(Parcel& output) const { - SAFE_PARCEL(output.writeInt64, vsyncId); - SAFE_PARCEL(output.writeInt32, inputEventId); - SAFE_PARCEL(output.writeInt64, startTimeNanos); - return NO_ERROR; -} - -status_t FrameTimelineInfo::read(const Parcel& input) { - SAFE_PARCEL(input.readInt64, &vsyncId); - SAFE_PARCEL(input.readInt32, &inputEventId); - SAFE_PARCEL(input.readInt64, &startTimeNanos); - return NO_ERROR; -} - -void FrameTimelineInfo::merge(const FrameTimelineInfo& other) { - // When merging vsync Ids we take the oldest valid one - if (vsyncId != INVALID_VSYNC_ID && other.vsyncId != INVALID_VSYNC_ID) { - if (other.vsyncId > vsyncId) { - vsyncId = other.vsyncId; - inputEventId = other.inputEventId; - startTimeNanos = other.startTimeNanos; - } - } else if (vsyncId == INVALID_VSYNC_ID) { - vsyncId = other.vsyncId; - inputEventId = other.inputEventId; - startTimeNanos = other.startTimeNanos; - } -} - -void FrameTimelineInfo::clear() { - vsyncId = INVALID_VSYNC_ID; - inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID; - startTimeNanos = 0; -} - -}; // namespace android diff --git a/libs/gui/GLConsumerUtils.cpp b/libs/gui/GLConsumerUtils.cpp index 7a06c3d801..a1c69e7d6d 100644 --- a/libs/gui/GLConsumerUtils.cpp +++ b/libs/gui/GLConsumerUtils.cpp @@ -27,6 +27,13 @@ namespace android { void GLConsumer::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform, bool filtering) { + computeTransformMatrix(outTransform, buf->getWidth(), buf->getHeight(), buf->getPixelFormat(), + cropRect, transform, filtering); +} + +void GLConsumer::computeTransformMatrix(float outTransform[16], float bufferWidth, + float bufferHeight, PixelFormat pixelFormat, + const Rect& cropRect, uint32_t transform, bool filtering) { // Transform matrices static const mat4 mtxFlipH( -1, 0, 0, 0, @@ -60,8 +67,6 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], if (!cropRect.isEmpty()) { float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); float shrinkAmount = 0.0f; if (filtering) { // In order to prevent bilinear sampling beyond the edge of the @@ -70,7 +75,7 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], // off each end, but because the chroma channels of YUV420 images // are subsampled we may need to shrink the crop region by a whole // texel on each side. - switch (buf->getPixelFormat()) { + switch (pixelFormat) { case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_RGBA_FP16: diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 24d39fe86a..a0e75ffe49 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -19,14 +19,11 @@ #include <android/gui/IDisplayEventConnection.h> #include <android/gui/IRegionSamplingListener.h> -#include <android/gui/ITransactionTraceListener.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> -#include <gui/ISurfaceComposerClient.h> -#include <gui/LayerDebugInfo.h> #include <gui/LayerState.h> #include <private/gui/ParcelUtils.h> #include <stdint.h> @@ -37,7 +34,6 @@ #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> #include <ui/HdrCapabilities.h> -#include <ui/StaticDisplayInfo.h> #include <utils/Log.h> // --------------------------------------------------------------------------- @@ -46,6 +42,7 @@ using namespace aidl::android::hardware::graphics; namespace android { +using gui::CallbackId; using gui::DisplayCaptureArgs; using gui::IDisplayEventConnection; using gui::IRegionSamplingListener; @@ -63,26 +60,18 @@ public: virtual ~BpSurfaceComposer(); - virtual sp<ISurfaceComposerClient> createConnection() - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); - return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); - } - status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, const InputWindowCommands& commands, - int64_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + Vector<ComposerState>& state, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& commands, int64_t desiredPresentTime, + bool isAutoTimestamp, const client_cache_t& uncacheBuffer, + bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(frameTimelineInfo.write, data); + frameTimelineInfo.writeToParcel(&data); SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size())); for (const auto& s : state) { @@ -119,905 +108,6 @@ public: data, &reply); } } - - void bootFinished() override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); - } - - bool authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& bufferProducer) const override { - Parcel data, reply; - int err = NO_ERROR; - err = data.writeInterfaceToken( - ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing " - "interface descriptor: %s (%d)", strerror(-err), -err); - return false; - } - err = data.writeStrongBinder(IInterface::asBinder(bufferProducer)); - if (err != NO_ERROR) { - ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing " - "strong binder to parcel: %s (%d)", strerror(-err), -err); - return false; - } - err = remote()->transact(BnSurfaceComposer::AUTHENTICATE_SURFACE, data, - &reply); - if (err != NO_ERROR) { - ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error " - "performing transaction: %s (%d)", strerror(-err), -err); - return false; - } - int32_t result = 0; - err = reply.readInt32(&result); - if (err != NO_ERROR) { - ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error " - "retrieving result: %s (%d)", strerror(-err), -err); - return false; - } - return result != 0; - } - - status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override { - if (!outSupported) { - return UNEXPECTED_NULL; - } - outSupported->clear(); - - Parcel data, reply; - - status_t err = data.writeInterfaceToken( - ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - return err; - } - - err = remote()->transact( - BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS, - data, &reply); - if (err != NO_ERROR) { - return err; - } - - int32_t result = 0; - err = reply.readInt32(&result); - if (err != NO_ERROR) { - return err; - } - if (result != NO_ERROR) { - return result; - } - - std::vector<int32_t> supported; - err = reply.readInt32Vector(&supported); - if (err != NO_ERROR) { - return err; - } - - outSupported->reserve(supported.size()); - for (int32_t s : supported) { - outSupported->push_back(static_cast<FrameEvent>(s)); - } - return NO_ERROR; - } - - sp<IDisplayEventConnection> createDisplayEventConnection( - VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) override { - Parcel data, reply; - sp<IDisplayEventConnection> result; - int err = data.writeInterfaceToken( - ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - return result; - } - data.writeInt32(static_cast<int32_t>(vsyncSource)); - data.writeUint32(eventRegistration.get()); - err = remote()->transact( - BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, - data, &reply); - if (err != NO_ERROR) { - ALOGE("ISurfaceComposer::createDisplayEventConnection: error performing " - "transaction: %s (%d)", strerror(-err), -err); - return result; - } - result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder()); - return result; - } - - status_t getStaticDisplayInfo(const sp<IBinder>& display, - ui::StaticDisplayInfo* info) override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_STATIC_DISPLAY_INFO, data, &reply); - const status_t result = reply.readInt32(); - if (result != NO_ERROR) return result; - return reply.read(*info); - } - - status_t getDynamicDisplayInfo(const sp<IBinder>& display, - ui::DynamicDisplayInfo* info) override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO, data, &reply); - const status_t result = reply.readInt32(); - if (result != NO_ERROR) return result; - return reply.read(*info); - } - - status_t getDisplayNativePrimaries(const sp<IBinder>& display, - ui::DisplayPrimaries& primaries) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply); - if (result != NO_ERROR) { - ALOGE("getDisplayNativePrimaries failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result == NO_ERROR) { - memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)), - sizeof(ui::DisplayPrimaries)); - } - return result; - } - - status_t setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("setActiveColorMode failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("setActiveColorMode failed to writeStrongBinder: %d", result); - return result; - } - result = data.writeInt32(static_cast<int32_t>(colorMode)); - if (result != NO_ERROR) { - ALOGE("setActiveColorMode failed to writeInt32: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, data, &reply); - if (result != NO_ERROR) { - ALOGE("setActiveColorMode failed to transact: %d", result); - return result; - } - return static_cast<status_t>(reply.readInt32()); - } - - status_t setBootDisplayMode(const sp<IBinder>& display, - ui::DisplayModeId displayModeId) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to writeStrongBinder: %d", result); - return result; - } - result = data.writeInt32(displayModeId); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to writeIint32: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::SET_BOOT_DISPLAY_MODE, data, &reply); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to transact: %d", result); - } - return result; - } - - status_t clearAnimationFrameStats() override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); - if (result != NO_ERROR) { - ALOGE("clearAnimationFrameStats failed to transact: %d", result); - return result; - } - return reply.readInt32(); - } - - status_t getAnimationFrameStats(FrameStats* outStats) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, data, &reply); - reply.read(*outStats); - return reply.readInt32(); - } - - virtual status_t overrideHdrTypes(const sp<IBinder>& display, - const std::vector<ui::Hdr>& hdrTypes) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, display); - - std::vector<int32_t> hdrTypesVector; - for (ui::Hdr i : hdrTypes) { - hdrTypesVector.push_back(static_cast<int32_t>(i)); - } - SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector); - - status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply); - if (result != NO_ERROR) { - ALOGE("overrideHdrTypes failed to transact: %d", result); - return result; - } - return result; - } - - status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeInt32, atomId); - - status_t err = remote()->transact(BnSurfaceComposer::ON_PULL_ATOM, data, &reply); - if (err != NO_ERROR) { - ALOGE("onPullAtom failed to transact: %d", err); - return err; - } - - int32_t size = 0; - SAFE_PARCEL(reply.readInt32, &size); - const void* dataPtr = reply.readInplace(size); - if (dataPtr == nullptr) { - return UNEXPECTED_NULL; - } - pulledData->assign((const char*)dataPtr, size); - SAFE_PARCEL(reply.readBool, success); - return NO_ERROR; - } - - status_t enableVSyncInjections(bool enable) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeBool(enable); - if (result != NO_ERROR) { - ALOGE("enableVSyncInjections failed to writeBool: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, data, &reply, - IBinder::FLAG_ONEWAY); - if (result != NO_ERROR) { - ALOGE("enableVSyncInjections failed to transact: %d", result); - return result; - } - return result; - } - - status_t injectVSync(nsecs_t when) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("injectVSync failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeInt64(when); - if (result != NO_ERROR) { - ALOGE("injectVSync failed to writeInt64: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, - IBinder::FLAG_ONEWAY); - if (result != NO_ERROR) { - ALOGE("injectVSync failed to transact: %d", result); - return result; - } - return result; - } - - status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override { - if (!outLayers) { - return UNEXPECTED_NULL; - } - - Parcel data, reply; - - status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - return err; - } - - err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply); - if (err != NO_ERROR) { - return err; - } - - int32_t result = 0; - err = reply.readInt32(&result); - if (err != NO_ERROR) { - return err; - } - if (result != NO_ERROR) { - return result; - } - - outLayers->clear(); - return reply.readParcelableVector(outLayers); - } - - status_t getCompositionPreference(ui::Dataspace* defaultDataspace, - ui::PixelFormat* defaultPixelFormat, - ui::Dataspace* wideColorGamutDataspace, - ui::PixelFormat* wideColorGamutPixelFormat) const override { - Parcel data, reply; - status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (error != NO_ERROR) { - return error; - } - error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply); - if (error != NO_ERROR) { - return error; - } - error = static_cast<status_t>(reply.readInt32()); - if (error == NO_ERROR) { - *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32()); - *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); - *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32()); - *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); - } - return error; - } - - status_t getColorManagement(bool* outGetColorManagement) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply); - bool result; - status_t err = reply.readBool(&result); - if (err == NO_ERROR) { - *outGetColorManagement = result; - } - return err; - } - - status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, - ui::PixelFormat* outFormat, - ui::Dataspace* outDataspace, - uint8_t* outComponentMask) const override { - if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE; - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - - status_t error = - remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, - data, &reply); - if (error != NO_ERROR) { - return error; - } - - uint32_t value = 0; - error = reply.readUint32(&value); - if (error != NO_ERROR) { - return error; - } - *outFormat = static_cast<ui::PixelFormat>(value); - - error = reply.readUint32(&value); - if (error != NO_ERROR) { - return error; - } - *outDataspace = static_cast<ui::Dataspace>(value); - - error = reply.readUint32(&value); - if (error != NO_ERROR) { - return error; - } - *outComponentMask = static_cast<uint8_t>(value); - return error; - } - - status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, - uint8_t componentMask, uint64_t maxFrames) override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeBool(enable); - data.writeByte(static_cast<int8_t>(componentMask)); - data.writeUint64(maxFrames); - status_t result = - remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data, - &reply); - return result; - } - - status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, - uint64_t timestamp, - DisplayedFrameStats* outStats) const override { - if (!outStats) return BAD_VALUE; - - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeUint64(maxFrames); - data.writeUint64(timestamp); - - status_t result = - remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply); - - if (result != NO_ERROR) { - return result; - } - - result = reply.readUint64(&outStats->numFrames); - if (result != NO_ERROR) { - return result; - } - - result = reply.readUint64Vector(&outStats->component_0_sample); - if (result != NO_ERROR) { - return result; - } - result = reply.readUint64Vector(&outStats->component_1_sample); - if (result != NO_ERROR) { - return result; - } - result = reply.readUint64Vector(&outStats->component_2_sample); - if (result != NO_ERROR) { - return result; - } - result = reply.readUint64Vector(&outStats->component_3_sample); - return result; - } - - status_t getProtectedContentSupport(bool* outSupported) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t error = - remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply); - if (error != NO_ERROR) { - return error; - } - error = reply.readBool(outSupported); - return error; - } - - status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, - const sp<IRegionSamplingListener>& listener) override { - Parcel data, reply; - status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write interface token"); - return error; - } - error = data.write(samplingArea); - if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write sampling area"); - return error; - } - error = data.writeStrongBinder(stopLayerHandle); - if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write stop layer handle"); - return error; - } - error = data.writeStrongBinder(IInterface::asBinder(listener)); - if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write listener"); - return error; - } - error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply); - if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to transact"); - } - return error; - } - - status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override { - Parcel data, reply; - status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (error != NO_ERROR) { - ALOGE("removeRegionSamplingListener: Failed to write interface token"); - return error; - } - error = data.writeStrongBinder(IInterface::asBinder(listener)); - if (error != NO_ERROR) { - ALOGE("removeRegionSamplingListener: Failed to write listener"); - return error; - } - error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data, - &reply); - if (error != NO_ERROR) { - ALOGE("removeRegionSamplingListener: Failed to transact"); - } - return error; - } - - virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeInt32, taskId); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); - const status_t error = - remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply); - if (error != OK) { - ALOGE("addFpsListener: Failed to transact"); - } - return error; - } - - virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); - - const status_t error = - remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply); - if (error != OK) { - ALOGE("removeFpsListener: Failed to transact"); - } - return error; - } - - virtual status_t addTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); - - const status_t error = - remote()->transact(BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER, data, - &reply); - if (error != NO_ERROR) { - ALOGE("addTunnelModeEnabledListener: Failed to transact"); - } - return error; - } - - virtual status_t removeTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); - - const status_t error = - remote()->transact(BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER, data, - &reply); - if (error != NO_ERROR) { - ALOGE("removeTunnelModeEnabledListener: Failed to transact"); - } - return error; - } - - status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(displayToken); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to write display token: %d", result); - return result; - } - result = data.writeInt32(defaultMode); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write defaultMode: %d", result); - return result; - } - result = data.writeBool(allowGroupSwitching); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write allowGroupSwitching: %d", result); - return result; - } - result = data.writeFloat(primaryRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMin: %d", result); - return result; - } - result = data.writeFloat(primaryRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMax: %d", result); - return result; - } - result = data.writeFloat(appRequestRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMin: %d", - result); - return result; - } - result = data.writeFloat(appRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMax: %d", - result); - return result; - } - - result = - remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, data, &reply); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs failed to transact: %d", result); - return result; - } - return reply.readInt32(); - } - - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) override { - if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin || - !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || - !outAppRequestRefreshRateMax) { - return BAD_VALUE; - } - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(displayToken); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to writeStrongBinder: %d", result); - return result; - } - result = - remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, data, &reply); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to transact: %d", result); - return result; - } - - result = reply.readInt32(outDefaultMode); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read defaultMode: %d", result); - return result; - } - if (*outDefaultMode < 0) { - ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, *outDefaultMode); - return BAD_VALUE; - } - - result = reply.readBool(outAllowGroupSwitching); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read allowGroupSwitching: %d", result); - return result; - } - result = reply.readFloat(outPrimaryRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMin: %d", result); - return result; - } - result = reply.readFloat(outPrimaryRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMax: %d", result); - return result; - } - result = reply.readFloat(outAppRequestRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMin: %d", result); - return result; - } - result = reply.readFloat(outAppRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMax: %d", result); - return result; - } - return reply.readInt32(); - } - - status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, - float lightPosY, float lightPosZ, float lightRadius) override { - Parcel data, reply; - status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (error != NO_ERROR) { - ALOGE("setGlobalShadowSettings: failed to write interface token: %d", error); - return error; - } - - std::vector<float> shadowConfig = {ambientColor.r, ambientColor.g, ambientColor.b, - ambientColor.a, spotColor.r, spotColor.g, - spotColor.b, spotColor.a, lightPosY, - lightPosZ, lightRadius}; - - error = data.writeFloatVector(shadowConfig); - if (error != NO_ERROR) { - ALOGE("setGlobalShadowSettings: failed to write shadowConfig: %d", error); - return error; - } - - error = remote()->transact(BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, data, &reply, - IBinder::FLAG_ONEWAY); - if (error != NO_ERROR) { - ALOGE("setGlobalShadowSettings: failed to transact: %d", error); - return error; - } - return NO_ERROR; - } - - status_t getDisplayDecorationSupport( - const sp<IBinder>& displayToken, - std::optional<common::DisplayDecorationSupport>* outSupport) const override { - Parcel data, reply; - status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to write interface token: %d", error); - return error; - } - error = data.writeStrongBinder(displayToken); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to write display token: %d", error); - return error; - } - error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_DECORATION_SUPPORT, data, &reply); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to transact: %d", error); - return error; - } - bool support; - error = reply.readBool(&support); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to read support: %d", error); - return error; - } - - if (support) { - int32_t format, alphaInterpretation; - error = reply.readInt32(&format); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to read format: %d", error); - return error; - } - error = reply.readInt32(&alphaInterpretation); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport: failed to read alphaInterpretation: %d", error); - return error; - } - outSupport->emplace(); - outSupport->value().format = static_cast<common::PixelFormat>(format); - outSupport->value().alphaInterpretation = - static_cast<common::AlphaInterpretation>(alphaInterpretation); - } else { - outSupport->reset(); - } - return NO_ERROR; - } - - status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility, int8_t changeFrameRateStrategy) override { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface)); - SAFE_PARCEL(data.writeFloat, frameRate); - SAFE_PARCEL(data.writeByte, compatibility); - SAFE_PARCEL(data.writeByte, changeFrameRateStrategy); - - status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply); - if (err != NO_ERROR) { - ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err); - return err; - } - - return reply.readInt32(); - } - - status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, - const FrameTimelineInfo& frameTimelineInfo) override { - Parcel data, reply; - status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (err != NO_ERROR) { - ALOGE("%s: failed writing interface token: %s (%d)", __func__, strerror(-err), -err); - return err; - } - - err = data.writeStrongBinder(IInterface::asBinder(surface)); - if (err != NO_ERROR) { - ALOGE("%s: failed writing strong binder: %s (%d)", __func__, strerror(-err), -err); - return err; - } - - SAFE_PARCEL(frameTimelineInfo.write, data); - - err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_INFO, data, &reply); - if (err != NO_ERROR) { - ALOGE("%s: failed to transact: %s (%d)", __func__, strerror(-err), err); - return err; - } - - return reply.readInt32(); - } - - status_t addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) override { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); - - return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply); - } - - /** - * Get priority of the RenderEngine in surface flinger. - */ - int getGPUContextPriority() override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t err = - remote()->transact(BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, data, &reply); - if (err != NO_ERROR) { - ALOGE("getGPUContextPriority failed to read data: %s (%d)", strerror(-err), err); - return 0; - } - return reply.readInt32(); - } - - status_t getMaxAcquiredBufferCount(int* buffers) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t err = - remote()->transact(BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); - if (err != NO_ERROR) { - ALOGE("getMaxAcquiredBufferCount failed to read data: %s (%d)", strerror(-err), err); - return err; - } - - return reply.readInt32(buffers); - } - - status_t addWindowInfosListener( - const sp<IWindowInfosListener>& windowInfosListener) const override { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener)); - return remote()->transact(BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, data, &reply); - } - - status_t removeWindowInfosListener( - const sp<IWindowInfosListener>& windowInfosListener) const override { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener)); - return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply); - } - - status_t setOverrideFrameRate(uid_t uid, float frameRate) override { - Parcel data, reply; - SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeUint32, uid); - SAFE_PARCEL(data.writeFloat, frameRate); - - status_t err = remote()->transact(BnSurfaceComposer::SET_OVERRIDE_FRAME_RATE, data, &reply); - if (err != NO_ERROR) { - ALOGE("setOverrideFrameRate: failed to transact %s (%d)", strerror(-err), err); - return err; - } - - return NO_ERROR; - } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -1031,18 +121,12 @@ IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch(code) { - case CREATE_CONNECTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> b = IInterface::asBinder(createConnection()); - reply->writeStrongBinder(b); - return NO_ERROR; - } + switch (code) { case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); FrameTimelineInfo frameTimelineInfo; - SAFE_PARCEL(frameTimelineInfo.read, data); + frameTimelineInfo.readFromParcel(&data); uint32_t count = 0; SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); @@ -1102,642 +186,6 @@ status_t BnSurfaceComposer::onTransact( uncachedBuffer, hasListenerCallbacks, listenerCallbacks, transactionId); } - case BOOT_FINISHED: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - bootFinished(); - return NO_ERROR; - } - case AUTHENTICATE_SURFACE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IGraphicBufferProducer> bufferProducer = - interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); - int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0; - reply->writeInt32(result); - return NO_ERROR; - } - case GET_SUPPORTED_FRAME_TIMESTAMPS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - std::vector<FrameEvent> supportedTimestamps; - status_t result = getSupportedFrameTimestamps(&supportedTimestamps); - status_t err = reply->writeInt32(result); - if (err != NO_ERROR) { - return err; - } - if (result != NO_ERROR) { - return result; - } - - std::vector<int32_t> supported; - supported.reserve(supportedTimestamps.size()); - for (FrameEvent s : supportedTimestamps) { - supported.push_back(static_cast<int32_t>(s)); - } - return reply->writeInt32Vector(supported); - } - case CREATE_DISPLAY_EVENT_CONNECTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()); - EventRegistrationFlags eventRegistration = - static_cast<EventRegistration>(data.readUint32()); - - sp<IDisplayEventConnection> connection( - createDisplayEventConnection(vsyncSource, eventRegistration)); - reply->writeStrongBinder(IInterface::asBinder(connection)); - return NO_ERROR; - } - case GET_STATIC_DISPLAY_INFO: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - ui::StaticDisplayInfo info; - const sp<IBinder> display = data.readStrongBinder(); - const status_t result = getStaticDisplayInfo(display, &info); - SAFE_PARCEL(reply->writeInt32, result); - if (result != NO_ERROR) return result; - SAFE_PARCEL(reply->write, info); - return NO_ERROR; - } - case GET_DYNAMIC_DISPLAY_INFO: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - ui::DynamicDisplayInfo info; - const sp<IBinder> display = data.readStrongBinder(); - const status_t result = getDynamicDisplayInfo(display, &info); - SAFE_PARCEL(reply->writeInt32, result); - if (result != NO_ERROR) return result; - SAFE_PARCEL(reply->write, info); - return NO_ERROR; - } - case GET_DISPLAY_NATIVE_PRIMARIES: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - ui::DisplayPrimaries primaries; - sp<IBinder> display = nullptr; - - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result); - return result; - } - - result = getDisplayNativePrimaries(display, primaries); - reply->writeInt32(result); - if (result == NO_ERROR) { - memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries, - sizeof(ui::DisplayPrimaries)); - } - - return NO_ERROR; - } - case SET_ACTIVE_COLOR_MODE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); - return result; - } - int32_t colorModeInt = 0; - result = data.readInt32(&colorModeInt); - if (result != NO_ERROR) { - ALOGE("setActiveColorMode failed to readInt32: %d", result); - return result; - } - result = setActiveColorMode(display, - static_cast<ColorMode>(colorModeInt)); - result = reply->writeInt32(result); - return result; - } - case SET_BOOT_DISPLAY_MODE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to readStrongBinder: %d", result); - return result; - } - ui::DisplayModeId displayModeId; - result = data.readInt32(&displayModeId); - if (result != NO_ERROR) { - ALOGE("setBootDisplayMode failed to readInt32: %d", result); - return result; - } - return setBootDisplayMode(display, displayModeId); - } - case CLEAR_ANIMATION_FRAME_STATS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - status_t result = clearAnimationFrameStats(); - reply->writeInt32(result); - return NO_ERROR; - } - case GET_ANIMATION_FRAME_STATS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - FrameStats stats; - status_t result = getAnimationFrameStats(&stats); - reply->write(stats); - reply->writeInt32(result); - return NO_ERROR; - } - case ENABLE_VSYNC_INJECTIONS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - bool enable = false; - status_t result = data.readBool(&enable); - if (result != NO_ERROR) { - ALOGE("enableVSyncInjections failed to readBool: %d", result); - return result; - } - return enableVSyncInjections(enable); - } - case INJECT_VSYNC: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int64_t when = 0; - status_t result = data.readInt64(&when); - if (result != NO_ERROR) { - ALOGE("enableVSyncInjections failed to readInt64: %d", result); - return result; - } - return injectVSync(when); - } - case GET_LAYER_DEBUG_INFO: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - std::vector<LayerDebugInfo> outLayers; - status_t result = getLayerDebugInfo(&outLayers); - reply->writeInt32(result); - if (result == NO_ERROR) - { - result = reply->writeParcelableVector(outLayers); - } - return result; - } - case GET_COMPOSITION_PREFERENCE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - ui::Dataspace defaultDataspace; - ui::PixelFormat defaultPixelFormat; - ui::Dataspace wideColorGamutDataspace; - ui::PixelFormat wideColorGamutPixelFormat; - status_t error = - getCompositionPreference(&defaultDataspace, &defaultPixelFormat, - &wideColorGamutDataspace, &wideColorGamutPixelFormat); - reply->writeInt32(error); - if (error == NO_ERROR) { - reply->writeInt32(static_cast<int32_t>(defaultDataspace)); - reply->writeInt32(static_cast<int32_t>(defaultPixelFormat)); - reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace)); - reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat)); - } - return error; - } - case GET_COLOR_MANAGEMENT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - bool result; - status_t error = getColorManagement(&result); - if (error == NO_ERROR) { - reply->writeBool(result); - } - return error; - } - case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - - sp<IBinder> display = data.readStrongBinder(); - ui::PixelFormat format; - ui::Dataspace dataspace; - uint8_t component = 0; - auto result = - getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component); - if (result == NO_ERROR) { - reply->writeUint32(static_cast<uint32_t>(format)); - reply->writeUint32(static_cast<uint32_t>(dataspace)); - reply->writeUint32(static_cast<uint32_t>(component)); - } - return result; - } - case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - - sp<IBinder> display = nullptr; - bool enable = false; - int8_t componentMask = 0; - uint64_t maxFrames = 0; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d", - result); - return result; - } - - result = data.readBool(&enable); - if (result != NO_ERROR) { - ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result); - return result; - } - - result = data.readByte(static_cast<int8_t*>(&componentMask)); - if (result != NO_ERROR) { - ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d", - result); - return result; - } - - result = data.readUint64(&maxFrames); - if (result != NO_ERROR) { - ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result); - return result; - } - - return setDisplayContentSamplingEnabled(display, enable, - static_cast<uint8_t>(componentMask), maxFrames); - } - case GET_DISPLAYED_CONTENT_SAMPLE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - - sp<IBinder> display = data.readStrongBinder(); - uint64_t maxFrames = 0; - uint64_t timestamp = 0; - - status_t result = data.readUint64(&maxFrames); - if (result != NO_ERROR) { - ALOGE("getDisplayedContentSample failure in reading max frames: %d", result); - return result; - } - - result = data.readUint64(×tamp); - if (result != NO_ERROR) { - ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result); - return result; - } - - DisplayedFrameStats stats; - result = getDisplayedContentSample(display, maxFrames, timestamp, &stats); - if (result == NO_ERROR) { - reply->writeUint64(stats.numFrames); - reply->writeUint64Vector(stats.component_0_sample); - reply->writeUint64Vector(stats.component_1_sample); - reply->writeUint64Vector(stats.component_2_sample); - reply->writeUint64Vector(stats.component_3_sample); - } - return result; - } - case GET_PROTECTED_CONTENT_SUPPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - bool result; - status_t error = getProtectedContentSupport(&result); - if (error == NO_ERROR) { - reply->writeBool(result); - } - return error; - } - case ADD_REGION_SAMPLING_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - Rect samplingArea; - status_t result = data.read(samplingArea); - if (result != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to read sampling area"); - return result; - } - sp<IBinder> stopLayerHandle; - result = data.readNullableStrongBinder(&stopLayerHandle); - if (result != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to read stop layer handle"); - return result; - } - sp<IRegionSamplingListener> listener; - result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to read listener"); - return result; - } - return addRegionSamplingListener(samplingArea, stopLayerHandle, listener); - } - case REMOVE_REGION_SAMPLING_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IRegionSamplingListener> listener; - status_t result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("removeRegionSamplingListener: Failed to read listener"); - return result; - } - return removeRegionSamplingListener(listener); - } - case ADD_FPS_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t taskId; - status_t result = data.readInt32(&taskId); - if (result != NO_ERROR) { - ALOGE("addFpsListener: Failed to read layer handle"); - return result; - } - sp<gui::IFpsListener> listener; - result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("addFpsListener: Failed to read listener"); - return result; - } - return addFpsListener(taskId, listener); - } - case REMOVE_FPS_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<gui::IFpsListener> listener; - status_t result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("removeFpsListener: Failed to read listener"); - return result; - } - return removeFpsListener(listener); - } - case ADD_TUNNEL_MODE_ENABLED_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<gui::ITunnelModeEnabledListener> listener; - status_t result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("addTunnelModeEnabledListener: Failed to read listener"); - return result; - } - return addTunnelModeEnabledListener(listener); - } - case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<gui::ITunnelModeEnabledListener> listener; - status_t result = data.readNullableStrongBinder(&listener); - if (result != NO_ERROR) { - ALOGE("removeTunnelModeEnabledListener: Failed to read listener"); - return result; - } - return removeTunnelModeEnabledListener(listener); - } - case SET_DESIRED_DISPLAY_MODE_SPECS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> displayToken = data.readStrongBinder(); - ui::DisplayModeId defaultMode; - status_t result = data.readInt32(&defaultMode); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read defaultMode: %d", result); - return result; - } - if (defaultMode < 0) { - ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode); - return BAD_VALUE; - } - bool allowGroupSwitching; - result = data.readBool(&allowGroupSwitching); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read allowGroupSwitching: %d", result); - return result; - } - float primaryRefreshRateMin; - result = data.readFloat(&primaryRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMin: %d", - result); - return result; - } - float primaryRefreshRateMax; - result = data.readFloat(&primaryRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMax: %d", - result); - return result; - } - float appRequestRefreshRateMin; - result = data.readFloat(&appRequestRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMin: %d", - result); - return result; - } - float appRequestRefreshRateMax; - result = data.readFloat(&appRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMax: %d", - result); - return result; - } - result = setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, - primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, appRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("setDesiredDisplayModeSpecs: failed to call setDesiredDisplayModeSpecs: " - "%d", - result); - return result; - } - reply->writeInt32(result); - return result; - } - case GET_DESIRED_DISPLAY_MODE_SPECS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> displayToken = data.readStrongBinder(); - ui::DisplayModeId defaultMode; - bool allowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - - status_t result = - getDesiredDisplayModeSpecs(displayToken, &defaultMode, &allowGroupSwitching, - &primaryRefreshRateMin, &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to get getDesiredDisplayModeSpecs: " - "%d", - result); - return result; - } - - result = reply->writeInt32(defaultMode); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write defaultMode: %d", result); - return result; - } - result = reply->writeBool(allowGroupSwitching); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write allowGroupSwitching: %d", - result); - return result; - } - result = reply->writeFloat(primaryRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMin: %d", - result); - return result; - } - result = reply->writeFloat(primaryRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMax: %d", - result); - return result; - } - result = reply->writeFloat(appRequestRefreshRateMin); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMin: %d", - result); - return result; - } - result = reply->writeFloat(appRequestRefreshRateMax); - if (result != NO_ERROR) { - ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMax: %d", - result); - return result; - } - reply->writeInt32(result); - return result; - } - case SET_GLOBAL_SHADOW_SETTINGS: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - - std::vector<float> shadowConfig; - status_t error = data.readFloatVector(&shadowConfig); - if (error != NO_ERROR || shadowConfig.size() != 11) { - ALOGE("setGlobalShadowSettings: failed to read shadowConfig: %d", error); - return error; - } - - half4 ambientColor = {shadowConfig[0], shadowConfig[1], shadowConfig[2], - shadowConfig[3]}; - half4 spotColor = {shadowConfig[4], shadowConfig[5], shadowConfig[6], shadowConfig[7]}; - float lightPosY = shadowConfig[8]; - float lightPosZ = shadowConfig[9]; - float lightRadius = shadowConfig[10]; - return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, - lightRadius); - } - case GET_DISPLAY_DECORATION_SUPPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> displayToken; - SAFE_PARCEL(data.readNullableStrongBinder, &displayToken); - std::optional<common::DisplayDecorationSupport> support; - auto error = getDisplayDecorationSupport(displayToken, &support); - if (error != NO_ERROR) { - ALOGE("getDisplayDecorationSupport failed with error %d", error); - return error; - } - reply->writeBool(support.has_value()); - if (support) { - reply->writeInt32(static_cast<int32_t>(support.value().format)); - reply->writeInt32(static_cast<int32_t>(support.value().alphaInterpretation)); - } - return error; - } - case SET_FRAME_RATE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> binder; - SAFE_PARCEL(data.readStrongBinder, &binder); - - sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder); - if (!surface) { - ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer"); - return BAD_VALUE; - } - float frameRate; - SAFE_PARCEL(data.readFloat, &frameRate); - - int8_t compatibility; - SAFE_PARCEL(data.readByte, &compatibility); - - int8_t changeFrameRateStrategy; - SAFE_PARCEL(data.readByte, &changeFrameRateStrategy); - - status_t result = - setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_FRAME_TIMELINE_INFO: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> binder; - status_t err = data.readStrongBinder(&binder); - if (err != NO_ERROR) { - ALOGE("setFrameTimelineInfo: failed to read strong binder: %s (%d)", strerror(-err), - -err); - return err; - } - sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder); - if (!surface) { - ALOGE("setFrameTimelineInfo: failed to cast to IGraphicBufferProducer: %s (%d)", - strerror(-err), -err); - return err; - } - - FrameTimelineInfo frameTimelineInfo; - SAFE_PARCEL(frameTimelineInfo.read, data); - - status_t result = setFrameTimelineInfo(surface, frameTimelineInfo); - reply->writeInt32(result); - return NO_ERROR; - } - case ADD_TRANSACTION_TRACE_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<gui::ITransactionTraceListener> listener; - SAFE_PARCEL(data.readStrongBinder, &listener); - - return addTransactionTraceListener(listener); - } - case GET_GPU_CONTEXT_PRIORITY: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int priority = getGPUContextPriority(); - SAFE_PARCEL(reply->writeInt32, priority); - return NO_ERROR; - } - case GET_MAX_ACQUIRED_BUFFER_COUNT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int buffers = 0; - int err = getMaxAcquiredBufferCount(&buffers); - if (err != NO_ERROR) { - return err; - } - SAFE_PARCEL(reply->writeInt32, buffers); - return NO_ERROR; - } - case OVERRIDE_HDR_TYPES: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - SAFE_PARCEL(data.readStrongBinder, &display); - - std::vector<int32_t> hdrTypes; - SAFE_PARCEL(data.readInt32Vector, &hdrTypes); - - std::vector<ui::Hdr> hdrTypesVector; - for (int i : hdrTypes) { - hdrTypesVector.push_back(static_cast<ui::Hdr>(i)); - } - return overrideHdrTypes(display, hdrTypesVector); - } - case ON_PULL_ATOM: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t atomId = 0; - SAFE_PARCEL(data.readInt32, &atomId); - - std::string pulledData; - bool success; - status_t err = onPullAtom(atomId, &pulledData, &success); - SAFE_PARCEL(reply->writeByteArray, pulledData.size(), - reinterpret_cast<const uint8_t*>(pulledData.data())); - SAFE_PARCEL(reply->writeBool, success); - return err; - } - case ADD_WINDOW_INFOS_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IWindowInfosListener> listener; - SAFE_PARCEL(data.readStrongBinder, &listener); - - return addWindowInfosListener(listener); - } - case REMOVE_WINDOW_INFOS_LISTENER: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IWindowInfosListener> listener; - SAFE_PARCEL(data.readStrongBinder, &listener); - - return removeWindowInfosListener(listener); - } - case SET_OVERRIDE_FRAME_RATE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - - uid_t uid; - SAFE_PARCEL(data.readUint32, &uid); - - float frameRate; - SAFE_PARCEL(data.readFloat, &frameRate); - - return setOverrideFrameRate(uid, frameRate); - } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp deleted file mode 100644 index 5e7a7ec67b..0000000000 --- a/libs/gui/ISurfaceComposerClient.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// tag as surfaceflinger -#define LOG_TAG "SurfaceFlinger" - -#include <gui/ISurfaceComposerClient.h> - -#include <gui/IGraphicBufferProducer.h> - -#include <binder/SafeInterface.h> - -#include <ui/FrameStats.h> - -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, - CREATE_WITH_SURFACE_PARENT, - CLEAR_LAYER_FRAME_STATS, - GET_LAYER_FRAME_STATS, - MIRROR_SURFACE, - LAST = MIRROR_SURFACE, -}; - -} // Anonymous namespace - -class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> { -public: - explicit BpSurfaceComposerClient(const sp<IBinder>& impl) - : SafeBpInterface<ISurfaceComposerClient>(impl, "BpSurfaceComposerClient") {} - - ~BpSurfaceComposerClient() override; - - status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format, - uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata, - sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - int32_t* outLayerId, uint32_t* outTransformHint) override { - return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE, - name, width, height, - format, flags, parent, - std::move(metadata), - handle, gbp, outLayerId, - outTransformHint); - } - - status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height, - PixelFormat format, uint32_t flags, - const sp<IGraphicBufferProducer>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint) override { - return callRemote<decltype( - &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT, - name, width, height, format, - flags, parent, - std::move(metadata), handle, gbp, - outLayerId, outTransformHint); - } - - status_t clearLayerFrameStats(const sp<IBinder>& handle) const override { - return callRemote<decltype( - &ISurfaceComposerClient::clearLayerFrameStats)>(Tag::CLEAR_LAYER_FRAME_STATS, - handle); - } - - status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const override { - return callRemote<decltype( - &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle, - outStats); - } - - status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, - int32_t* outLayerId) override { - return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE, - mirrorFromHandle, - outHandle, outLayerId); - } -}; - -// Out-of-line virtual method definition to trigger vtable emission in this -// translation unit (see clang warning -Wweak-vtables) -BpSurfaceComposerClient::~BpSurfaceComposerClient() {} - -IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); - -// ---------------------------------------------------------------------- - -status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::CREATE_SURFACE: - return callLocal(data, reply, &ISurfaceComposerClient::createSurface); - case Tag::CREATE_WITH_SURFACE_PARENT: - return callLocal(data, reply, &ISurfaceComposerClient::createWithSurfaceParent); - case Tag::CLEAR_LAYER_FRAME_STATS: - return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats); - case Tag::GET_LAYER_FRAME_STATS: - return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats); - case Tag::MIRROR_SURFACE: - return callLocal(data, reply, &ISurfaceComposerClient::mirrorSurface); - } -} - -} // namespace android diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index e4b8bad8f8..23d7d500c8 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -17,23 +17,15 @@ #define LOG_TAG "ITransactionCompletedListener" //#define LOG_NDEBUG 0 +#include <cstdint> +#include <optional> + #include <gui/ISurfaceComposer.h> -#include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> +#include <gui/ListenerStats.h> #include <private/gui/ParcelUtils.h> -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, - ON_RELEASE_BUFFER, - ON_TRANSACTION_QUEUE_STALLED, - LAST = ON_RELEASE_BUFFER, -}; - -} // Anonymous namespace +namespace android::gui { status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); @@ -126,7 +118,12 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const { } else { SAFE_PARCEL(output->writeBool, false); } - SAFE_PARCEL(output->writeUint32, transformHint); + + SAFE_PARCEL(output->writeBool, transformHint.has_value()); + if (transformHint.has_value()) { + output->writeUint32(transformHint.value()); + } + SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeParcelable, eventStats); SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size())); @@ -156,7 +153,16 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { previousReleaseFence = new Fence(); SAFE_PARCEL(input->read, *previousReleaseFence); } - SAFE_PARCEL(input->readUint32, &transformHint); + bool hasTransformHint = false; + SAFE_PARCEL(input->readBool, &hasTransformHint); + if (hasTransformHint) { + uint32_t tempTransformHint; + SAFE_PARCEL(input->readUint32, &tempTransformHint); + transformHint = std::make_optional(tempTransformHint); + } else { + transformHint = std::nullopt; + } + SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readParcelable, &eventStats); @@ -257,58 +263,6 @@ ListenerStats ListenerStats::createEmpty( return listenerStats; } -class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> { -public: - explicit BpTransactionCompletedListener(const sp<IBinder>& impl) - : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") { - } - - ~BpTransactionCompletedListener() override; - - void onTransactionCompleted(ListenerStats stats) override { - callRemoteAsync<decltype(&ITransactionCompletedListener:: - onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED, - stats); - } - - void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence, - uint32_t currentMaxAcquiredBufferCount) override { - callRemoteAsync<decltype( - &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, - callbackId, releaseFence, - currentMaxAcquiredBufferCount); - } - - void onTransactionQueueStalled() override { - callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionQueueStalled)>( - Tag::ON_TRANSACTION_QUEUE_STALLED); - } -}; - -// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see -// clang warning -Wweak-vtables) -BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; - -IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); - -status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::ON_TRANSACTION_COMPLETED: - return callLocalAsync(data, reply, - &ITransactionCompletedListener::onTransactionCompleted); - case Tag::ON_RELEASE_BUFFER: - return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer); - case Tag::ON_TRANSACTION_QUEUE_STALLED: - return callLocalAsync(data, reply, - &ITransactionCompletedListener::onTransactionQueueStalled); - } -} - ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const { std::vector<CallbackId> filteredCallbackIds; for (const auto& callbackId : callbackIds) { @@ -347,4 +301,4 @@ status_t ReleaseCallbackId::readFromParcel(const Parcel* input) { const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0); -}; // namespace android +}; // namespace android::gui diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index ea5fb293a6..15b2221464 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -27,7 +27,7 @@ using android::base::StringAppendF; #define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) -namespace android { +namespace android::gui { status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); @@ -149,4 +149,4 @@ std::string to_string(const LayerDebugInfo& info) { return result; } -} // android +} // namespace android::gui diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 189d51a4c1..4e12fd330c 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -23,7 +23,7 @@ using android::base::StringPrintf; -namespace android { +namespace android::gui { LayerMetadata::LayerMetadata() = default; @@ -144,4 +144,4 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con } } -} // namespace android +} // namespace android::gui diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 502031c8d8..0d1a69b898 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -19,17 +19,39 @@ #include <cinttypes> #include <cmath> +#include <android/gui/ISurfaceComposerClient.h> #include <android/native_window.h> #include <binder/Parcel.h> #include <gui/IGraphicBufferProducer.h> -#include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> +#include <gui/SurfaceControl.h> #include <private/gui/ParcelUtils.h> #include <system/window.h> #include <utils/Errors.h> +#define CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD) \ + { \ + if ((OTHER.what & CHANGE_FLAG) && (FIELD != OTHER.FIELD)) { \ + DIFF_RESULT |= CHANGE_FLAG; \ + } \ + } + +#define CHECK_DIFF2(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2) \ + { \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \ + } + +#define CHECK_DIFF3(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2, FIELD3) \ + { \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD3) \ + } + namespace android { +using gui::CallbackId; using gui::FocusRequest; using gui::WindowInfoHandle; @@ -40,15 +62,13 @@ layer_state_t::layer_state_t() x(0), y(0), z(0), - w(0), - h(0), - alpha(0), flags(0), mask(0), reserved(0), cornerRadius(0.0f), backgroundBlurRadius(0), - transform(0), + color(0), + bufferTransform(0), transformToDisplayInverse(false), crop(Rect::INVALID_RECT), dataspace(ui::Dataspace::UNKNOWN), @@ -63,9 +83,11 @@ layer_state_t::layer_state_t() frameRate(0.0f), frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS), + defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), fixedTransformHint(ui::Transform::ROT_INVALID), autoRefresh(false), isTrustedOverlay(false), + borderEnabled(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), dropInputMode(gui::DropInputMode::NONE) { @@ -82,25 +104,27 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, x); SAFE_PARCEL(output.writeFloat, y); SAFE_PARCEL(output.writeInt32, z); - SAFE_PARCEL(output.writeUint32, w); - SAFE_PARCEL(output.writeUint32, h); SAFE_PARCEL(output.writeUint32, layerStack.id); - SAFE_PARCEL(output.writeFloat, alpha); SAFE_PARCEL(output.writeUint32, flags); SAFE_PARCEL(output.writeUint32, mask); SAFE_PARCEL(matrix.write, output); SAFE_PARCEL(output.write, crop); - SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl); SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); SAFE_PARCEL(output.writeFloat, color.r); SAFE_PARCEL(output.writeFloat, color.g); SAFE_PARCEL(output.writeFloat, color.b); + SAFE_PARCEL(output.writeFloat, color.a); SAFE_PARCEL(windowInfoHandle->writeToParcel, &output); SAFE_PARCEL(output.write, transparentRegion); - SAFE_PARCEL(output.writeUint32, transform); + SAFE_PARCEL(output.writeUint32, bufferTransform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); - + SAFE_PARCEL(output.writeBool, borderEnabled); + SAFE_PARCEL(output.writeFloat, borderWidth); + SAFE_PARCEL(output.writeFloat, borderColor.r); + SAFE_PARCEL(output.writeFloat, borderColor.g); + SAFE_PARCEL(output.writeFloat, borderColor.b); + SAFE_PARCEL(output.writeFloat, borderColor.a); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); SAFE_PARCEL(output.write, surfaceDamageRegion); @@ -131,6 +155,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, frameRate); SAFE_PARCEL(output.writeByte, frameRateCompatibility); SAFE_PARCEL(output.writeByte, changeFrameRateStrategy); + SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility); SAFE_PARCEL(output.writeUint32, fixedTransformHint); SAFE_PARCEL(output.writeBool, autoRefresh); SAFE_PARCEL(output.writeBool, dimmingEnabled); @@ -172,10 +197,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &x); SAFE_PARCEL(input.readFloat, &y); SAFE_PARCEL(input.readInt32, &z); - SAFE_PARCEL(input.readUint32, &w); - SAFE_PARCEL(input.readUint32, &h); SAFE_PARCEL(input.readUint32, &layerStack.id); - SAFE_PARCEL(input.readFloat, &alpha); SAFE_PARCEL(input.readUint32, &flags); @@ -183,7 +205,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(matrix.read, input); SAFE_PARCEL(input.read, crop); - SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl); SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); @@ -195,11 +216,25 @@ status_t layer_state_t::read(const Parcel& input) color.g = tmpFloat; SAFE_PARCEL(input.readFloat, &tmpFloat); color.b = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.a = tmpFloat; + SAFE_PARCEL(windowInfoHandle->readFromParcel, &input); SAFE_PARCEL(input.read, transparentRegion); - SAFE_PARCEL(input.readUint32, &transform); + SAFE_PARCEL(input.readUint32, &bufferTransform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); + SAFE_PARCEL(input.readBool, &borderEnabled); + SAFE_PARCEL(input.readFloat, &tmpFloat); + borderWidth = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + borderColor.r = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + borderColor.g = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + borderColor.b = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + borderColor.a = tmpFloat; uint32_t tmpUint32 = 0; SAFE_PARCEL(input.readUint32, &tmpUint32); @@ -240,6 +275,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &frameRate); SAFE_PARCEL(input.readByte, &frameRateCompatibility); SAFE_PARCEL(input.readByte, &changeFrameRateStrategy); + SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility); SAFE_PARCEL(input.readUint32, &tmpUint32); fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); SAFE_PARCEL(input.readBool, &autoRefresh); @@ -353,6 +389,27 @@ void DisplayState::merge(const DisplayState& other) { } } +void DisplayState::sanitize(int32_t permissions) { + if (what & DisplayState::eLayerStackChanged) { + if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~DisplayState::eLayerStackChanged; + ALOGE("Stripped attempt to set eLayerStackChanged in sanitize"); + } + } + if (what & DisplayState::eDisplayProjectionChanged) { + if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~DisplayState::eDisplayProjectionChanged; + ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize"); + } + } + if (what & DisplayState::eSurfaceChanged) { + if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~DisplayState::eSurfaceChanged; + ALOGE("Stripped attempt to set eSurfaceChanged in sanitize"); + } + } +} + void layer_state_t::sanitize(int32_t permissions) { // TODO: b/109894387 // @@ -437,14 +494,9 @@ void layer_state_t::merge(const layer_state_t& other) { what &= ~eRelativeLayerChanged; z = other.z; } - if (other.what & eSizeChanged) { - what |= eSizeChanged; - w = other.w; - h = other.h; - } if (other.what & eAlphaChanged) { what |= eAlphaChanged; - alpha = other.alpha; + color.a = other.color.a; } if (other.what & eMatrixChanged) { what |= eMatrixChanged; @@ -486,12 +538,9 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eReparent; parentSurfaceControlForChild = other.parentSurfaceControlForChild; } - if (other.what & eDestroySurface) { - what |= eDestroySurface; - } - if (other.what & eTransformChanged) { - what |= eTransformChanged; - transform = other.transform; + if (other.what & eBufferTransformChanged) { + what |= eBufferTransformChanged; + bufferTransform = other.bufferTransform; } if (other.what & eTransformToDisplayInverseChanged) { what |= eTransformToDisplayInverseChanged; @@ -538,7 +587,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; - color = other.color; + color.rgb = other.color.rgb; bgColorAlpha = other.bgColorAlpha; bgColorDataspace = other.bgColorDataspace; } @@ -550,6 +599,16 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eShadowRadiusChanged; shadowRadius = other.shadowRadius; } + if (other.what & eRenderBorderChanged) { + what |= eRenderBorderChanged; + borderEnabled = other.borderEnabled; + borderWidth = other.borderWidth; + borderColor = other.borderColor; + } + if (other.what & eDefaultFrameRateCompatibilityChanged) { + what |= eDefaultFrameRateCompatibilityChanged; + defaultFrameRateCompatibility = other.defaultFrameRateCompatibility; + } if (other.what & eFrameRateSelectionPriority) { what |= eFrameRateSelectionPriority; frameRateSelectionPriority = other.frameRateSelectionPriority; @@ -593,7 +652,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eColorChanged) { what |= eColorChanged; - color = other.color; + color.rgb = other.color.rgb; } if (other.what & eColorSpaceAgnosticChanged) { what |= eColorSpaceAgnosticChanged; @@ -610,12 +669,80 @@ void layer_state_t::merge(const layer_state_t& other) { } } +uint64_t layer_state_t::diff(const layer_state_t& other) const { + uint64_t diff = 0; + CHECK_DIFF2(diff, ePositionChanged, other, x, y); + if (other.what & eLayerChanged) { + diff |= eLayerChanged; + diff &= ~eRelativeLayerChanged; + } + CHECK_DIFF(diff, eAlphaChanged, other, color.a); + CHECK_DIFF(diff, eMatrixChanged, other, matrix); + if (other.what & eTransparentRegionChanged && + (!transparentRegion.hasSameRects(other.transparentRegion))) { + diff |= eTransparentRegionChanged; + } + if (other.what & eFlagsChanged) { + uint64_t changedFlags = (flags & other.mask) ^ (other.flags & other.mask); + if (changedFlags) diff |= eFlagsChanged; + } + CHECK_DIFF(diff, eLayerStackChanged, other, layerStack); + CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius); + CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius); + if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged; + if (other.what & eRelativeLayerChanged) { + diff |= eRelativeLayerChanged; + diff &= ~eLayerChanged; + } + if (other.what & eReparent && + !SurfaceControl::isSameSurface(parentSurfaceControlForChild, + other.parentSurfaceControlForChild)) { + diff |= eReparent; + } + CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform); + CHECK_DIFF(diff, eTransformToDisplayInverseChanged, other, transformToDisplayInverse); + CHECK_DIFF(diff, eCropChanged, other, crop); + if (other.what & eBufferChanged) diff |= eBufferChanged; + CHECK_DIFF(diff, eDataspaceChanged, other, dataspace); + CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); + if (other.what & eSurfaceDamageRegionChanged && + (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) { + diff |= eSurfaceDamageRegionChanged; + } + CHECK_DIFF(diff, eApiChanged, other, api); + if (other.what & eSidebandStreamChanged) diff |= eSidebandStreamChanged; + CHECK_DIFF(diff, eApiChanged, other, api); + CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform); + if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged; + if (other.what & eInputInfoChanged) diff |= eInputInfoChanged; + CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace); + if (other.what & eMetadataChanged) diff |= eMetadataChanged; + CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius); + CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor); + CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility); + CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority); + CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility, + changeFrameRateStrategy); + CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint); + CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh); + CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay); + CHECK_DIFF(diff, eStretchChanged, other, stretchEffect); + CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop); + CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame); + if (other.what & eProducerDisconnect) diff |= eProducerDisconnect; + CHECK_DIFF(diff, eDropInputModeChanged, other, dropInputMode); + CHECK_DIFF(diff, eColorChanged, other, color.rgb); + CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic); + CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); + return diff; +} + bool layer_state_t::hasBufferChanges() const { return what & layer_state_t::eBufferChanged; } bool layer_state_t::hasValidBuffer() const { - return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid()); + return bufferData && (bufferData->hasBuffer() || bufferData->cachedBuffer.isValid()); } status_t layer_state_t::matrix22_t::write(Parcel& output) const { @@ -641,29 +768,44 @@ bool InputWindowCommands::merge(const InputWindowCommands& other) { changes |= !other.focusRequests.empty(); focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()), std::make_move_iterator(other.focusRequests.end())); - changes |= other.syncInputWindows && !syncInputWindows; - syncInputWindows |= other.syncInputWindows; + changes |= !other.windowInfosReportedListeners.empty(); + windowInfosReportedListeners.insert(other.windowInfosReportedListeners.begin(), + other.windowInfosReportedListeners.end()); return changes; } bool InputWindowCommands::empty() const { - return focusRequests.empty() && !syncInputWindows; + return focusRequests.empty() && windowInfosReportedListeners.empty(); } void InputWindowCommands::clear() { focusRequests.clear(); - syncInputWindows = false; + windowInfosReportedListeners.clear(); } status_t InputWindowCommands::write(Parcel& output) const { SAFE_PARCEL(output.writeParcelableVector, focusRequests); - SAFE_PARCEL(output.writeBool, syncInputWindows); + + SAFE_PARCEL(output.writeInt32, windowInfosReportedListeners.size()); + for (const auto& listener : windowInfosReportedListeners) { + SAFE_PARCEL(output.writeStrongBinder, listener); + } + return NO_ERROR; } status_t InputWindowCommands::read(const Parcel& input) { SAFE_PARCEL(input.readParcelableVector, &focusRequests); - SAFE_PARCEL(input.readBool, &syncInputWindows); + + int listenerSize = 0; + SAFE_PARCEL_READ_SIZE(input.readInt32, &listenerSize, input.dataSize()); + windowInfosReportedListeners.reserve(listenerSize); + for (int i = 0; i < listenerSize; i++) { + sp<gui::IWindowInfosReportedListener> listener; + SAFE_PARCEL(input.readStrongBinder, &listener); + windowInfosReportedListeners.insert(listener); + } + return NO_ERROR; } diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp index fe387064bc..601a5f9b33 100644 --- a/libs/gui/ScreenCaptureResults.cpp +++ b/libs/gui/ScreenCaptureResults.cpp @@ -17,6 +17,7 @@ #include <gui/ScreenCaptureResults.h> #include <private/gui/ParcelUtils.h> +#include <ui/FenceResult.h> namespace android::gui { @@ -28,17 +29,17 @@ status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const { SAFE_PARCEL(parcel->writeBool, false); } - if (fence != Fence::NO_FENCE) { + if (fenceResult.ok() && fenceResult.value() != Fence::NO_FENCE) { SAFE_PARCEL(parcel->writeBool, true); - SAFE_PARCEL(parcel->write, *fence); + SAFE_PARCEL(parcel->write, *fenceResult.value()); } else { SAFE_PARCEL(parcel->writeBool, false); + SAFE_PARCEL(parcel->writeInt32, fenceStatus(fenceResult)); } SAFE_PARCEL(parcel->writeBool, capturedSecureLayers); SAFE_PARCEL(parcel->writeBool, capturedHdrLayers); SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace)); - SAFE_PARCEL(parcel->writeInt32, result); return NO_ERROR; } @@ -53,8 +54,13 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { bool hasFence; SAFE_PARCEL(parcel->readBool, &hasFence); if (hasFence) { - fence = new Fence(); - SAFE_PARCEL(parcel->read, *fence); + fenceResult = sp<Fence>::make(); + SAFE_PARCEL(parcel->read, *fenceResult.value()); + } else { + status_t status; + SAFE_PARCEL(parcel->readInt32, &status); + fenceResult = status == NO_ERROR ? FenceResult(Fence::NO_FENCE) + : FenceResult(base::unexpected(status)); } SAFE_PARCEL(parcel->readBool, &capturedSecureLayers); @@ -62,7 +68,6 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { uint32_t dataspace = 0; SAFE_PARCEL(parcel->readUint32, &dataspace); capturedDataspace = static_cast<ui::Dataspace>(dataspace); - SAFE_PARCEL(parcel->readInt32, &result); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 3b137080ab..edb18a86ee 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -30,15 +30,17 @@ #include <android/gui/DisplayStatInfo.h> #include <android/native_window.h> +#include <gui/TraceUtils.h> #include <utils/Log.h> -#include <utils/Trace.h> #include <utils/NativeHandle.h> +#include <utils/Trace.h> #include <ui/DynamicDisplayInfo.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <ui/Region.h> +#include <gui/AidlStatusUtil.h> #include <gui/BufferItem.h> #include <gui/IProducerListener.h> @@ -49,10 +51,17 @@ namespace android { +using gui::aidl_utils::statusTFromBinderStatus; using ui::Dataspace; namespace { +enum { + // moved from nativewindow/include/system/window.h, to be removed + NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, + NATIVE_WINDOW_GET_HDR_SUPPORT = 29, +}; + bool isInterceptorRegistrationOp(int op) { return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR || op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR || @@ -182,7 +191,7 @@ status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { gui::DisplayStatInfo stats; binder::Status status = composerServiceAIDL()->getDisplayStats(nullptr, &stats); if (!status.isOk()) { - return status.transactionError(); + return statusTFromBinderStatus(status); } *outRefreshDuration = stats.vsyncPeriod; @@ -345,33 +354,25 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, return NO_ERROR; } +// Deprecated(b/242763577): to be removed, this method should not be used +// The reason this method still exists here is to support compiled vndk +// Surface support should not be tied to the display +// Return true since most displays should have this support status_t Surface::getWideColorSupport(bool* supported) { ATRACE_CALL(); - const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken(); - if (display == nullptr) { - return NAME_NOT_FOUND; - } - - *supported = false; - binder::Status status = composerServiceAIDL()->isWideColorDisplay(display, supported); - return status.transactionError(); + *supported = true; + return NO_ERROR; } +// Deprecated(b/242763577): to be removed, this method should not be used +// The reason this method still exists here is to support compiled vndk +// Surface support should not be tied to the display +// Return true since most displays should have this support status_t Surface::getHdrSupport(bool* supported) { ATRACE_CALL(); - const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken(); - if (display == nullptr) { - return NAME_NOT_FOUND; - } - - ui::DynamicDisplayInfo info; - if (status_t err = composerService()->getDynamicDisplayInfo(display, &info); err != NO_ERROR) { - return err; - } - - *supported = !info.hdrCapabilities.getSupportedHdrTypes().empty(); + *supported = true; return NO_ERROR; } @@ -634,7 +635,7 @@ void Surface::getDequeueBufferInputLocked( } int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { - ATRACE_CALL(); + ATRACE_FORMAT("dequeueBuffer - %s", getDebugName()); ALOGV("Surface::dequeueBuffer"); IGraphicBufferProducer::DequeueBufferInput dqInput; @@ -1259,10 +1260,10 @@ void Surface::querySupportedTimestampsLocked() const { mQueriedSupportedTimestamps = true; std::vector<FrameEvent> supportedFrameTimestamps; - status_t err = composerService()->getSupportedFrameTimestamps( - &supportedFrameTimestamps); + binder::Status status = + composerServiceAIDL()->getSupportedFrameTimestamps(&supportedFrameTimestamps); - if (err != NO_ERROR) { + if (!status.isOk()) { return; } @@ -1290,15 +1291,12 @@ int Surface::query(int what, int* value) const { if (err == NO_ERROR) { return NO_ERROR; } - sp<ISurfaceComposer> surfaceComposer = composerService(); + sp<gui::ISurfaceComposer> surfaceComposer = composerServiceAIDL(); if (surfaceComposer == nullptr) { return -EPERM; // likely permissions error } - if (surfaceComposer->authenticateSurfaceTexture(mGraphicBufferProducer)) { - *value = 1; - } else { - *value = 0; - } + // ISurfaceComposer no longer supports authenticateSurfaceTexture + *value = 0; return NO_ERROR; } case NATIVE_WINDOW_CONCRETE_TYPE: @@ -1870,7 +1868,11 @@ int Surface::dispatchSetFrameTimelineInfo(va_list args) { auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t)); ALOGV("Surface::%s", __func__); - return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos}); + FrameTimelineInfo ftlInfo; + ftlInfo.vsyncId = frameTimelineVsyncId; + ftlInfo.inputEventId = inputEventId; + ftlInfo.startTimeNanos = startTimeNanos; + return setFrameTimelineInfo(ftlInfo); } bool Surface::transformToDisplayInverse() const { @@ -2630,22 +2632,18 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ mSurfaceListener->onBuffersDiscarded(discardedBufs); } -status_t Surface::setFrameRate(float frameRate, int8_t compatibility, - int8_t changeFrameRateStrategy) { - ATRACE_CALL(); - ALOGV("Surface::setFrameRate"); - - if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy, - "Surface::setFrameRate")) { - return BAD_VALUE; - } - - return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility, - changeFrameRateStrategy); +[[deprecated]] status_t Surface::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/, + int8_t /*changeFrameRateStrategy*/) { + ALOGI("Surface::setFrameRate is deprecated, setFrameRate hint is dropped as destination is not " + "SurfaceFlinger"); + // ISurfaceComposer no longer supports setFrameRate, we will return NO_ERROR when the api is + // called to avoid apps crashing, as BAD_VALUE can generate fatal exception in apps. + return NO_ERROR; } -status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { - return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo); +status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& /*frameTimelineInfo*/) { + // ISurfaceComposer no longer supports setFrameTimelineInfo + return BAD_VALUE; } sp<IBinder> Surface::getSurfaceControlHandle() const { @@ -2658,4 +2656,12 @@ void Surface::destroy() { mSurfaceControlHandle = nullptr; } +const char* Surface::getDebugName() { + std::unique_lock lock{mNameMutex}; + if (mName.empty()) { + mName = getConsumerName(); + } + return mName.c_str(); +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 0f5192d41c..d741c99d01 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -16,11 +16,16 @@ #define LOG_TAG "SurfaceComposerClient" +#include <semaphore.h> #include <stdint.h> #include <sys/types.h> +#include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/DisplayState.h> +#include <android/gui/ISurfaceComposerClient.h> #include <android/gui/IWindowInfosListener.h> +#include <android/os/IInputConstants.h> +#include <gui/TraceUtils.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/SortedVector.h> @@ -33,11 +38,11 @@ #include <system/graphics.h> +#include <gui/AidlStatusUtil.h> #include <gui/BufferItemConsumer.h> #include <gui/CpuConsumer.h> #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> -#include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -61,6 +66,7 @@ using gui::IRegionSamplingListener; using gui::WindowInfo; using gui::WindowInfoHandle; using gui::WindowInfosListener; +using gui::aidl_utils::statusTFromBinderStatus; using ui::ColorMode; // --------------------------------------------------------------------------- @@ -111,7 +117,6 @@ bool ComposerService::connectLocked() { if (instance.mComposerService == nullptr) { if (ComposerService::getInstance().connectLocked()) { ALOGD("ComposerService reconnected"); - WindowInfosListenerReporter::getInstance()->reconnect(instance.mComposerService); } } return instance.mComposerService; @@ -159,6 +164,7 @@ bool ComposerServiceAIDL::connectLocked() { if (instance.mComposerService == nullptr) { if (ComposerServiceAIDL::getInstance().connectLocked()) { ALOGD("ComposerServiceAIDL reconnected"); + WindowInfosListenerReporter::getInstance()->reconnect(instance.mComposerService); } } return instance.mComposerService; @@ -308,7 +314,8 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( } } -void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { +binder::Status TransactionCompletedListener::onTransactionCompleted( + const ListenerStats& listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { @@ -380,10 +387,11 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener surfaceStats.previousReleaseFence, surfaceStats.transformHint, surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount); - if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) { + if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl] && + surfaceStats.transformHint.has_value()) { callbacksMap[callbackId] .surfaceControls[surfaceStats.surfaceControl] - ->setTransformHint(surfaceStats.transformHint); + ->setTransformHint(*surfaceStats.transformHint); } // If there is buffer id set, we look up any pending client release buffer callbacks // and call them. This is a performance optimization when we have a transaction @@ -447,32 +455,38 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } } } + return binder::Status::ok(); } -void TransactionCompletedListener::onTransactionQueueStalled() { - std::unordered_map<void*, std::function<void()>> callbackCopy; - { - std::scoped_lock<std::mutex> lock(mMutex); - callbackCopy = mQueueStallListeners; - } - for (auto const& it : callbackCopy) { - it.second(); - } +binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) { + std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy; + { + std::scoped_lock<std::mutex> lock(mMutex); + callbackCopy = mQueueStallListeners; + } + for (auto const& it : callbackCopy) { + it.second(reason.c_str()); + } + return binder::Status::ok(); } -void TransactionCompletedListener::addQueueStallListener(std::function<void()> stallListener, - void* id) { +void TransactionCompletedListener::addQueueStallListener( + std::function<void(const std::string&)> stallListener, void* id) { std::scoped_lock<std::mutex> lock(mMutex); mQueueStallListeners[id] = stallListener; } -void TransactionCompletedListener::removeQueueStallListener(void *id) { + +void TransactionCompletedListener::removeQueueStallListener(void* id) { std::scoped_lock<std::mutex> lock(mMutex); mQueueStallListeners.erase(id); } -void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, - sp<Fence> releaseFence, - uint32_t currentMaxAcquiredBufferCount) { +binder::Status TransactionCompletedListener::onReleaseBuffer( + const ReleaseCallbackId& callbackId, + const std::optional<os::ParcelFileDescriptor>& releaseFenceFd, + int32_t currentMaxAcquiredBufferCount) { + sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get())) + : Fence::NO_FENCE); ReleaseBufferCallback callback; { std::scoped_lock<std::mutex> lock(mMutex); @@ -481,13 +495,14 @@ void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, if (!callback) { ALOGE("Could not call release buffer callback, buffer not found %s", callbackId.to_string().c_str()); - return; + return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT); } std::optional<uint32_t> optionalMaxAcquiredBufferCount = - currentMaxAcquiredBufferCount == UINT_MAX + static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX ? std::nullopt : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount); callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount); + return binder::Status::ok(); } ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked( @@ -625,12 +640,11 @@ SurfaceComposerClient::Transaction::Transaction() { SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : mId(other.mId), - mForceSynchronous(other.mForceSynchronous), mTransactionNestCount(other.mTransactionNestCount), mAnimation(other.mAnimation), mEarlyWakeupStart(other.mEarlyWakeupStart), mEarlyWakeupEnd(other.mEarlyWakeupEnd), - mContainsBuffer(other.mContainsBuffer), + mMayContainBuffer(other.mMayContainBuffer), mDesiredPresentTime(other.mDesiredPresentTime), mIsAutoTimestamp(other.mIsAutoTimestamp), mFrameTimelineInfo(other.mFrameTimelineInfo), @@ -659,16 +673,15 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { - const uint32_t forceSynchronous = parcel->readUint32(); + const uint64_t transactionId = parcel->readUint64(); const uint32_t transactionNestCount = parcel->readUint32(); const bool animation = parcel->readBool(); const bool earlyWakeupStart = parcel->readBool(); const bool earlyWakeupEnd = parcel->readBool(); - const bool containsBuffer = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); FrameTimelineInfo frameTimelineInfo; - SAFE_PARCEL(frameTimelineInfo.read, *parcel); + frameTimelineInfo.readFromParcel(parcel); sp<IBinder> applyToken; parcel->readNullableStrongBinder(&applyToken); @@ -736,12 +749,11 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel inputWindowCommands.read(*parcel); // Parsing was successful. Update the object. - mForceSynchronous = forceSynchronous; + mId = transactionId; mTransactionNestCount = transactionNestCount; mAnimation = animation; mEarlyWakeupStart = earlyWakeupStart; mEarlyWakeupEnd = earlyWakeupEnd; - mContainsBuffer = containsBuffer; mDesiredPresentTime = desiredPresentTime; mIsAutoTimestamp = isAutoTimestamp; mFrameTimelineInfo = frameTimelineInfo; @@ -767,15 +779,14 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers(); - parcel->writeUint32(mForceSynchronous); + parcel->writeUint64(mId); parcel->writeUint32(mTransactionNestCount); parcel->writeBool(mAnimation); parcel->writeBool(mEarlyWakeupStart); parcel->writeBool(mEarlyWakeupEnd); - parcel->writeBool(mContainsBuffer); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); - SAFE_PARCEL(mFrameTimelineInfo.write, *parcel); + mFrameTimelineInfo.writeToParcel(parcel); parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); for (auto const& displayState : mDisplayStates) { @@ -821,7 +832,11 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_ ->mReleaseCallbackThread .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence); } else { - listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX); + std::optional<os::ParcelFileDescriptor> fenceFd; + if (fence != Fence::NO_FENCE) { + fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get()))); + } + listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX); } } @@ -871,12 +886,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mInputWindowCommands.merge(other.mInputWindowCommands); - mContainsBuffer |= other.mContainsBuffer; + mMayContainBuffer |= other.mMayContainBuffer; mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart; mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd; mApplyToken = other.mApplyToken; - mFrameTimelineInfo.merge(other.mFrameTimelineInfo); + mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); other.clear(); return *this; @@ -887,15 +902,14 @@ void SurfaceComposerClient::Transaction::clear() { mDisplayStates.clear(); mListenerCallbacks.clear(); mInputWindowCommands.clear(); - mContainsBuffer = false; - mForceSynchronous = 0; + mMayContainBuffer = false; mTransactionNestCount = 0; mAnimation = false; mEarlyWakeupStart = false; mEarlyWakeupEnd = false; mDesiredPresentTime = 0; mIsAutoTimestamp = true; - mFrameTimelineInfo.clear(); + clearFrameTimelineInfo(mFrameTimelineInfo); mApplyToken = nullptr; } @@ -909,14 +923,19 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { client_cache_t uncacheBuffer; uncacheBuffer.token = BufferCache::getInstance().getToken(); uncacheBuffer.id = cacheId; - - sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true, - uncacheBuffer, false, {}, generateId()); + Vector<ComposerState> composerStates; + status_t status = + sf->setTransactionState(FrameTimelineInfo{}, composerStates, {}, + ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(), + {}, systemTime(), true, uncacheBuffer, false, {}, generateId()); + if (status != NO_ERROR) { + ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s", + strerror(-status)); + } } void SurfaceComposerClient::Transaction::cacheBuffers() { - if (!mContainsBuffer) { + if (!mMayContainBuffer) { return; } @@ -961,12 +980,55 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } } +class SyncCallback { +public: + static void function(void* callbackContext, nsecs_t /* latchTime */, + const sp<Fence>& /* presentFence */, + const std::vector<SurfaceControlStats>& /* stats */) { + if (!callbackContext) { + ALOGE("failed to get callback context for SyncCallback"); + } + SyncCallback* helper = static_cast<SyncCallback*>(callbackContext); + LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed"); + } + ~SyncCallback() { + if (mInitialized) { + LOG_ALWAYS_FATAL_IF(sem_destroy(&mSemaphore), "sem_destroy failed"); + } + } + void init() { + LOG_ALWAYS_FATAL_IF(clock_gettime(CLOCK_MONOTONIC, &mTimeoutTimespec) == -1, + "clock_gettime() fail! in SyncCallback::init"); + mTimeoutTimespec.tv_sec += 4; + LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed"); + mInitialized = true; + } + void wait() { + int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &mTimeoutTimespec); + if (result && errno != ETIMEDOUT && errno != EINTR) { + LOG_ALWAYS_FATAL("sem_clockwait failed(%d)", errno); + } else if (errno == ETIMEDOUT) { + ALOGW("Sync transaction timed out waiting for commit callback."); + } + } + void* getContext() { return static_cast<void*>(this); } + +private: + sem_t mSemaphore; + bool mInitialized = false; + timespec mTimeoutTimespec; +}; + status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) { if (mStatus != NO_ERROR) { return mStatus; } - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + SyncCallback syncCallback; + if (synchronous) { + syncCallback.init(); + addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext()); + } bool hasListenerCallbacks = !mListenerCallbacks.empty(); std::vector<ListenerCallbacks> listenerCallbacks; @@ -1001,27 +1063,22 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay Vector<DisplayState> displayStates; uint32_t flags = 0; - mForceSynchronous |= synchronous; - - for (auto const& kv : mComposerStates){ + for (auto const& kv : mComposerStates) { composerStates.add(kv.second); } displayStates = std::move(mDisplayStates); - if (mForceSynchronous) { - flags |= ISurfaceComposer::eSynchronous; - } if (mAnimation) { flags |= ISurfaceComposer::eAnimation; } if (oneWay) { - if (mForceSynchronous) { - ALOGE("Transaction attempted to set synchronous and one way at the same time" - " this is an invalid request. Synchronous will win for safety"); - } else { - flags |= ISurfaceComposer::eOneWay; - } + if (synchronous) { + ALOGE("Transaction attempted to set synchronous and one way at the same time" + " this is an invalid request. Synchronous will win for safety"); + } else { + flags |= ISurfaceComposer::eOneWay; + } } // If both mEarlyWakeupStart and mEarlyWakeupEnd are set @@ -1033,10 +1090,9 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay flags |= ISurfaceComposer::eEarlyWakeupEnd; } - sp<IBinder> applyToken = mApplyToken - ? mApplyToken - : IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sp<IBinder> applyToken = mApplyToken ? mApplyToken : sApplyToken; + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken, mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, @@ -1046,10 +1102,23 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay // Clear the current states and flags clear(); + if (synchronous) { + syncCallback.wait(); + } + mStatus = NO_ERROR; return NO_ERROR; } +sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); + +sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() { + return sApplyToken; +} + +void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) { + sApplyToken = applyToken; +} // --------------------------------------------------------------------------- sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, bool secure) { @@ -1080,21 +1149,6 @@ std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() { return physicalDisplayIds; } -status_t SurfaceComposerClient::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) { - int64_t displayId; - binder::Status status = - ComposerServiceAIDL::getComposerService()->getPrimaryPhysicalDisplayId(&displayId); - if (status.isOk()) { - *id = *DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId)); - } - return status.transactionError(); -} - -std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() { - ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance(); - return instance.getInternalDisplayId(); -} - sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) { sp<IBinder> display = nullptr; binder::Status status = @@ -1103,11 +1157,6 @@ sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId dis return status.isOk() ? display : nullptr; } -sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() { - ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance(); - return instance.getInternalDisplayToken(); -} - void SurfaceComposerClient::Transaction::setAnimationTransaction() { mAnimation = true; } @@ -1170,21 +1219,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::hide( return setFlags(sc, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize( - const sp<SurfaceControl>& sc, uint32_t w, uint32_t h) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - s->what |= layer_state_t::eSizeChanged; - s->w = w; - s->h = h; - - registerSurfaceControlForCallback(sc); - return *this; -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer( const sp<SurfaceControl>& sc, int32_t z) { layer_state_t* s = getLayerState(sc); @@ -1227,6 +1261,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) || (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) || (mask & layer_state_t::eEnableBackpressure) || + (mask & layer_state_t::eIgnoreDestinationFrame) || (mask & layer_state_t::eLayerIsDisplayDecoration)) { s->what |= layer_state_t::eFlagsChanged; } @@ -1274,8 +1309,11 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha mStatus = BAD_INDEX; return *this; } + if (alpha < 0.0f || alpha > 1.0f) { + ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha); + } s->what |= layer_state_t::eAlphaChanged; - s->alpha = alpha; + s->color.a = std::clamp(alpha, 0.f, 1.f); registerSurfaceControlForCallback(sc); return *this; @@ -1406,7 +1444,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor return *this; } s->what |= layer_state_t::eColorChanged; - s->color = color; + s->color.rgb = color; registerSurfaceControlForCallback(sc); return *this; @@ -1421,7 +1459,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackg } s->what |= layer_state_t::eBackgroundColorChanged; - s->color = color; + s->color.rgb = color; s->bgColorAlpha = alpha; s->bgColorDataspace = dataspace; @@ -1436,8 +1474,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrans mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eTransformChanged; - s->transform = transform; + s->what |= layer_state_t::eBufferTransformChanged; + s->bufferTransform = transform; registerSurfaceControlForCallback(sc); return *this; @@ -1475,7 +1513,6 @@ std::shared_ptr<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffe s->what &= ~layer_state_t::eBufferChanged; s->bufferData = nullptr; - mContainsBuffer = false; return bufferData; } @@ -1506,7 +1543,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe if (buffer == nullptr) { s->what &= ~layer_state_t::eBufferChanged; s->bufferData = nullptr; - mContainsBuffer = false; return *this; } @@ -1541,7 +1577,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe const std::vector<SurfaceControlStats>&) {}, nullptr); - mContainsBuffer = true; + mMayContainBuffer = true; return *this; } @@ -1729,8 +1765,10 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocus return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() { - mInputWindowCommands.syncInputWindows = true; +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::addWindowInfosReportedListener( + sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) { + mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener); return *this; } @@ -1837,6 +1875,19 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame return *this; } +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc, + int8_t compatibility) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDefaultFrameRateCompatibilityChanged; + s->defaultFrameRateCompatibility = compatibility; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint( const sp<SurfaceControl>& sc, int32_t fixedTransformHint) { layer_state_t* s = getLayerState(sc); @@ -1855,7 +1906,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo( const FrameTimelineInfo& frameTimelineInfo) { - mFrameTimelineInfo.merge(frameTimelineInfo); + mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo); return *this; } @@ -1949,6 +2000,23 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder( + const sp<SurfaceControl>& sc, bool shouldEnable, float width, const half4& color) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eRenderBorderChanged; + s->borderEnabled = shouldEnable; + s->borderWidth = width; + s->borderColor = color; + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -2004,7 +2072,6 @@ void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& s.layerStackSpaceRect = layerStackRect; s.orientedDisplaySpaceRect = displayRect; s.what |= DisplayState::eDisplayProjectionChanged; - mForceSynchronous = true; // TODO: do we actually still need this? } void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) { @@ -2014,6 +2081,31 @@ void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token s.what |= DisplayState::eDisplaySizeChanged; } +// copied from FrameTimelineInfo::merge() +void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t, + const FrameTimelineInfo& other) { + // When merging vsync Ids we take the oldest valid one + if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID && + other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { + if (other.vsyncId > t.vsyncId) { + t.vsyncId = other.vsyncId; + t.inputEventId = other.inputEventId; + t.startTimeNanos = other.startTimeNanos; + } + } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { + t.vsyncId = other.vsyncId; + t.inputEventId = other.inputEventId; + t.startTimeNanos = other.startTimeNanos; + } +} + +// copied from FrameTimelineInfo::clear() +void SurfaceComposerClient::Transaction::clearFrameTimelineInfo(FrameTimelineInfo& t) { + t.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID; + t.inputEventId = os::IInputConstants::INVALID_INPUT_EVENT_ID; + t.startTimeNanos = 0; +} + // --------------------------------------------------------------------------- SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {} @@ -2022,11 +2114,11 @@ SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& c : mStatus(NO_ERROR), mClient(client) {} void SurfaceComposerClient::onFirstRef() { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); if (sf != nullptr && mStatus == NO_INIT) { sp<ISurfaceComposerClient> conn; - conn = sf->createConnection(); - if (conn != nullptr) { + binder::Status status = sf->createConnection(&conn); + if (status.isOk() && conn != nullptr) { mClient = conn; mStatus = NO_ERROR; } @@ -2064,7 +2156,7 @@ void SurfaceComposerClient::dispose() { } sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h, - PixelFormat format, uint32_t flags, + PixelFormat format, int32_t flags, const sp<IBinder>& parentHandle, LayerMetadata metadata, uint32_t* outTransformHint) { @@ -2074,38 +2166,13 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uin return s; } -sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w, - uint32_t h, PixelFormat format, - uint32_t flags, Surface* parent, - LayerMetadata metadata, - uint32_t* outTransformHint) { - sp<SurfaceControl> sur; - status_t err = mStatus; - - if (mStatus == NO_ERROR) { - sp<IBinder> handle; - sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer(); - sp<IGraphicBufferProducer> gbp; - - uint32_t transformHint = 0; - int32_t id = -1; - err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, - std::move(metadata), &handle, &gbp, &id, - &transformHint); - if (outTransformHint) { - *outTransformHint = transformHint; - } - ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err)); - if (err == NO_ERROR) { - return new SurfaceControl(this, handle, gbp, id, transformHint); - } - } - return nullptr; +static std::string toString(const String16& string) { + return std::string(String8(string).c_str()); } status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h, PixelFormat format, - sp<SurfaceControl>* outSurface, uint32_t flags, + sp<SurfaceControl>* outSurface, int32_t flags, const sp<IBinder>& parentHandle, LayerMetadata metadata, uint32_t* outTransformHint) { @@ -2113,21 +2180,18 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 status_t err = mStatus; if (mStatus == NO_ERROR) { - sp<IBinder> handle; - sp<IGraphicBufferProducer> gbp; - - uint32_t transformHint = 0; - int32_t id = -1; - err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), - &handle, &gbp, &id, &transformHint); - + gui::CreateSurfaceResult result; + binder::Status status = mClient->createSurface(std::string(name.string()), flags, + parentHandle, std::move(metadata), &result); + err = statusTFromBinderStatus(status); if (outTransformHint) { - *outTransformHint = transformHint; + *outTransformHint = result.transformHint; } ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { - *outSurface = - new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags); + *outSurface = new SurfaceControl(this, result.handle, result.layerId, + toString(result.layerName), w, h, format, + result.transformHint, flags); } } return err; @@ -2138,12 +2202,22 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFr return nullptr; } - sp<IBinder> handle; sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle(); - int32_t layer_id = -1; - status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id); + gui::CreateSurfaceResult result; + const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result); + const status_t err = statusTFromBinderStatus(status); if (err == NO_ERROR) { - return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */); + return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); + } + return nullptr; +} + +sp<SurfaceControl> SurfaceComposerClient::mirrorDisplay(DisplayId displayId) { + gui::CreateSurfaceResult result; + const binder::Status status = mClient->mirrorDisplay(displayId.value, &result); + const status_t err = statusTFromBinderStatus(status); + if (err == NO_ERROR) { + return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); } return nullptr; } @@ -2152,7 +2226,8 @@ status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) c if (mStatus != NO_ERROR) { return mStatus; } - return mClient->clearLayerFrameStats(token); + const binder::Status status = mClient->clearLayerFrameStats(token); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token, @@ -2160,21 +2235,28 @@ status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token, if (mStatus != NO_ERROR) { return mStatus; } - return mClient->getLayerFrameStats(token, outStats); + gui::FrameStats stats; + const binder::Status status = mClient->getLayerFrameStats(token, &stats); + if (status.isOk()) { + outStats->refreshPeriodNano = stats.refreshPeriodNano; + outStats->desiredPresentTimesNano.setCapacity(stats.desiredPresentTimesNano.size()); + for (const auto& t : stats.desiredPresentTimesNano) { + outStats->desiredPresentTimesNano.add(t); + } + outStats->actualPresentTimesNano.setCapacity(stats.actualPresentTimesNano.size()); + for (const auto& t : stats.actualPresentTimesNano) { + outStats->actualPresentTimesNano.add(t); + } + outStats->frameReadyTimesNano.setCapacity(stats.frameReadyTimesNano.size()); + for (const auto& t : stats.frameReadyTimesNano) { + outStats->frameReadyTimesNano.add(t); + } + } + return statusTFromBinderStatus(status); } // ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::enableVSyncInjections(bool enable) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - return sf->enableVSyncInjections(enable); -} - -status_t SurfaceComposerClient::injectVSync(nsecs_t when) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - return sf->injectVSync(when); -} - status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) { gui::DisplayState ds; @@ -2186,23 +2268,135 @@ status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display, state->layerStackSpaceRect = ui::Size(ds.layerStackSpaceRect.width, ds.layerStackSpaceRect.height); } - return status.transactionError(); + return statusTFromBinderStatus(status); } -status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display, - ui::StaticDisplayInfo* info) { - return ComposerService::getComposerService()->getStaticDisplayInfo(display, info); +status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId, + ui::StaticDisplayInfo* outInfo) { + using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag; + gui::StaticDisplayInfo ginfo; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo); + if (status.isOk()) { + // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo + outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType); + outInfo->density = ginfo.density; + outInfo->secure = ginfo.secure; + outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation); + + DeviceProductInfo info; + std::optional<gui::DeviceProductInfo> dpi = ginfo.deviceProductInfo; + gui::DeviceProductInfo::ManufactureOrModelDate& date = dpi->manufactureOrModelDate; + info.name = dpi->name; + if (dpi->manufacturerPnpId.size() > 0) { + // copid from PnpId = std::array<char, 4> in ui/DeviceProductInfo.h + constexpr int kMaxPnpIdSize = 4; + size_t count = std::max<size_t>(kMaxPnpIdSize, dpi->manufacturerPnpId.size()); + std::copy_n(dpi->manufacturerPnpId.begin(), count, info.manufacturerPnpId.begin()); + } + if (dpi->relativeAddress.size() > 0) { + std::copy(dpi->relativeAddress.begin(), dpi->relativeAddress.end(), + std::back_inserter(info.relativeAddress)); + } + info.productId = dpi->productId; + if (date.getTag() == Tag::modelYear) { + DeviceProductInfo::ModelYear modelYear; + modelYear.year = static_cast<uint32_t>(date.get<Tag::modelYear>().year); + info.manufactureOrModelDate = modelYear; + } else if (date.getTag() == Tag::manufactureYear) { + DeviceProductInfo::ManufactureYear manufactureYear; + manufactureYear.year = date.get<Tag::manufactureYear>().modelYear.year; + info.manufactureOrModelDate = manufactureYear; + } else if (date.getTag() == Tag::manufactureWeekAndYear) { + DeviceProductInfo::ManufactureWeekAndYear weekAndYear; + weekAndYear.year = + date.get<Tag::manufactureWeekAndYear>().manufactureYear.modelYear.year; + weekAndYear.week = date.get<Tag::manufactureWeekAndYear>().week; + info.manufactureOrModelDate = weekAndYear; + } + + outInfo->deviceProductInfo = info; + } + return statusTFromBinderStatus(status); } -status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display, - ui::DynamicDisplayInfo* info) { - return ComposerService::getComposerService()->getDynamicDisplayInfo(display, info); +void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo, + ui::DynamicDisplayInfo*& outInfo) { + // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo + outInfo->supportedDisplayModes.clear(); + outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size()); + for (const auto& mode : ginfo.supportedDisplayModes) { + ui::DisplayMode outMode; + outMode.id = mode.id; + outMode.resolution.width = mode.resolution.width; + outMode.resolution.height = mode.resolution.height; + outMode.xDpi = mode.xDpi; + outMode.yDpi = mode.yDpi; + outMode.refreshRate = mode.refreshRate; + outMode.appVsyncOffset = mode.appVsyncOffset; + outMode.sfVsyncOffset = mode.sfVsyncOffset; + outMode.presentationDeadline = mode.presentationDeadline; + outMode.group = mode.group; + std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(), + std::back_inserter(outMode.supportedHdrTypes), + [](const int32_t& value) { return static_cast<ui::Hdr>(value); }); + outInfo->supportedDisplayModes.push_back(outMode); + } + + outInfo->activeDisplayModeId = ginfo.activeDisplayModeId; + outInfo->renderFrameRate = ginfo.renderFrameRate; + + outInfo->supportedColorModes.clear(); + outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size()); + for (const auto& cmode : ginfo.supportedColorModes) { + outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode)); + } + + outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode); + + std::vector<ui::Hdr> types; + types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size()); + for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) { + types.push_back(static_cast<ui::Hdr>(hdr)); + } + outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance, + ginfo.hdrCapabilities.maxAverageLuminance, + ginfo.hdrCapabilities.minLuminance); + + outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported; + outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported; + outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode; +} + +status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId, + ui::DynamicDisplayInfo* outInfo) { + gui::DynamicDisplayInfo ginfo; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId, + &ginfo); + if (status.isOk()) { + getDynamicDisplayInfoInternal(ginfo, outInfo); + } + return statusTFromBinderStatus(status); +} + +status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display, + ui::DynamicDisplayInfo* outInfo) { + gui::DynamicDisplayInfo ginfo; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display, + &ginfo); + if (status.isOk()) { + getDynamicDisplayInfoInternal(ginfo, outInfo); + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode* mode) { ui::DynamicDisplayInfo info; - status_t result = getDynamicDisplayInfo(display, &info); + + status_t result = getDynamicDisplayInfoFromToken(display, &info); if (result != NO_ERROR) { return result; } @@ -2216,58 +2410,87 @@ status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display, return NAME_NOT_FOUND; } -status_t SurfaceComposerClient::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { - return ComposerService::getComposerService() - ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, - primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, appRequestRefreshRateMax); +status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->setDesiredDisplayModeSpecs(displayToken, + specs); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { - return ComposerService::getComposerService() - ->getDesiredDisplayModeSpecs(displayToken, outDefaultMode, outAllowGroupSwitching, - outPrimaryRefreshRateMin, outPrimaryRefreshRateMax, - outAppRequestRefreshRateMin, outAppRequestRefreshRateMax); + gui::DisplayModeSpecs* outSpecs) { + if (!outSpecs) { + return BAD_VALUE; + } + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDesiredDisplayModeSpecs(displayToken, + outSpecs); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display, ui::DisplayPrimaries& outPrimaries) { - return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries); + gui::DisplayPrimaries primaries; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDisplayNativePrimaries(display, + &primaries); + if (status.isOk()) { + outPrimaries.red.X = primaries.red.X; + outPrimaries.red.Y = primaries.red.Y; + outPrimaries.red.Z = primaries.red.Z; + + outPrimaries.green.X = primaries.green.X; + outPrimaries.green.Y = primaries.green.Y; + outPrimaries.green.Z = primaries.green.Z; + + outPrimaries.blue.X = primaries.blue.X; + outPrimaries.blue.Y = primaries.blue.Y; + outPrimaries.blue.Z = primaries.blue.Z; + + outPrimaries.white.X = primaries.white.X; + outPrimaries.white.Y = primaries.white.Y; + outPrimaries.white.Z = primaries.white.Z; + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) { - return ComposerService::getComposerService()->setActiveColorMode(display, colorMode); + binder::Status status = ComposerServiceAIDL::getComposerService() + ->setActiveColorMode(display, static_cast<int>(colorMode)); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getBootDisplayModeSupport(bool* support) { binder::Status status = ComposerServiceAIDL::getComposerService()->getBootDisplayModeSupport(support); - return status.transactionError(); + return statusTFromBinderStatus(status); +} + +status_t SurfaceComposerClient::getOverlaySupport(gui::OverlayProperties* outProperties) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->getOverlaySupport(outProperties); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId displayModeId) { - return ComposerService::getComposerService()->setBootDisplayMode(display, displayModeId); + binder::Status status = ComposerServiceAIDL::getComposerService() + ->setBootDisplayMode(display, static_cast<int>(displayModeId)); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) { binder::Status status = ComposerServiceAIDL::getComposerService()->clearBootDisplayMode(display); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) { - return ComposerService::getComposerService()->setOverrideFrameRate(uid, frameRate); + binder::Status status = + ComposerServiceAIDL::getComposerService()->setOverrideFrameRate(uid, frameRate); + return statusTFromBinderStatus(status); } void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) { @@ -2286,57 +2509,137 @@ void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token, status_t SurfaceComposerClient::getCompositionPreference( ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) { - return ComposerService::getComposerService() - ->getCompositionPreference(defaultDataspace, defaultPixelFormat, - wideColorGamutDataspace, wideColorGamutPixelFormat); + gui::CompositionPreference pref; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getCompositionPreference(&pref); + if (status.isOk()) { + *defaultDataspace = static_cast<ui::Dataspace>(pref.defaultDataspace); + *defaultPixelFormat = static_cast<ui::PixelFormat>(pref.defaultPixelFormat); + *wideColorGamutDataspace = static_cast<ui::Dataspace>(pref.wideColorGamutDataspace); + *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(pref.wideColorGamutPixelFormat); + } + return statusTFromBinderStatus(status); } bool SurfaceComposerClient::getProtectedContentSupport() { bool supported = false; - ComposerService::getComposerService()->getProtectedContentSupport(&supported); + ComposerServiceAIDL::getComposerService()->getProtectedContentSupport(&supported); return supported; } status_t SurfaceComposerClient::clearAnimationFrameStats() { - return ComposerService::getComposerService()->clearAnimationFrameStats(); + binder::Status status = ComposerServiceAIDL::getComposerService()->clearAnimationFrameStats(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getAnimationFrameStats(FrameStats* outStats) { - return ComposerService::getComposerService()->getAnimationFrameStats(outStats); + gui::FrameStats stats; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getAnimationFrameStats(&stats); + if (status.isOk()) { + outStats->refreshPeriodNano = stats.refreshPeriodNano; + outStats->desiredPresentTimesNano.setCapacity(stats.desiredPresentTimesNano.size()); + for (const auto& t : stats.desiredPresentTimesNano) { + outStats->desiredPresentTimesNano.add(t); + } + outStats->actualPresentTimesNano.setCapacity(stats.actualPresentTimesNano.size()); + for (const auto& t : stats.actualPresentTimesNano) { + outStats->actualPresentTimesNano.add(t); + } + outStats->frameReadyTimesNano.setCapacity(stats.frameReadyTimesNano.size()); + for (const auto& t : stats.frameReadyTimesNano) { + outStats->frameReadyTimesNano.add(t); + } + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display, const std::vector<ui::Hdr>& hdrTypes) { - return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes); + std::vector<int32_t> hdrTypesVector; + hdrTypesVector.reserve(hdrTypes.size()); + for (auto t : hdrTypes) { + hdrTypesVector.push_back(static_cast<int32_t>(t)); + } + + binder::Status status = + ComposerServiceAIDL::getComposerService()->overrideHdrTypes(display, hdrTypesVector); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::onPullAtom(const int32_t atomId, std::string* outData, bool* success) { - return ComposerService::getComposerService()->onPullAtom(atomId, outData, success); + gui::PullAtomData pad; + binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad); + if (status.isOk()) { + outData->assign(pad.data.begin(), pad.data.end()); + *success = pad.success; + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, uint8_t* outComponentMask) { - return ComposerService::getComposerService() - ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace, - outComponentMask); + if (!outFormat || !outDataspace || !outComponentMask) { + return BAD_VALUE; + } + + gui::ContentSamplingAttributes attrs; + binder::Status status = ComposerServiceAIDL::getComposerService() + ->getDisplayedContentSamplingAttributes(display, &attrs); + if (status.isOk()) { + *outFormat = static_cast<ui::PixelFormat>(attrs.format); + *outDataspace = static_cast<ui::Dataspace>(attrs.dataspace); + *outComponentMask = static_cast<uint8_t>(attrs.componentMask); + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, uint8_t componentMask, uint64_t maxFrames) { - return ComposerService::getComposerService()->setDisplayContentSamplingEnabled(display, enable, - componentMask, - maxFrames); + binder::Status status = + ComposerServiceAIDL::getComposerService() + ->setDisplayContentSamplingEnabled(display, enable, + static_cast<int8_t>(componentMask), + static_cast<int64_t>(maxFrames)); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) { - return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames, - timestamp, outStats); + if (!outStats) { + return BAD_VALUE; + } + + gui::DisplayedFrameStats stats; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDisplayedContentSample(display, maxFrames, + timestamp, &stats); + if (status.isOk()) { + // convert gui::DisplayedFrameStats to ui::DisplayedFrameStats + outStats->numFrames = static_cast<uint64_t>(stats.numFrames); + outStats->component_0_sample.reserve(stats.component_0_sample.size()); + for (const auto& s : stats.component_0_sample) { + outStats->component_0_sample.push_back(static_cast<uint64_t>(s)); + } + outStats->component_1_sample.reserve(stats.component_1_sample.size()); + for (const auto& s : stats.component_1_sample) { + outStats->component_1_sample.push_back(static_cast<uint64_t>(s)); + } + outStats->component_2_sample.reserve(stats.component_2_sample.size()); + for (const auto& s : stats.component_2_sample) { + outStats->component_2_sample.push_back(static_cast<uint64_t>(s)); + } + outStats->component_3_sample.reserve(stats.component_3_sample.size()); + for (const auto& s : stats.component_3_sample) { + outStats->component_3_sample.push_back(static_cast<uint64_t>(s)); + } + } + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display, @@ -2344,39 +2647,55 @@ status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display, binder::Status status = ComposerServiceAIDL::getComposerService()->isWideColorDisplay(display, outIsWideColorDisplay); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::addRegionSamplingListener( const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, const sp<IRegionSamplingListener>& listener) { - return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea, - stopLayerHandle, - listener); + gui::ARect rect; + rect.left = samplingArea.left; + rect.top = samplingArea.top; + rect.right = samplingArea.right; + rect.bottom = samplingArea.bottom; + binder::Status status = + ComposerServiceAIDL::getComposerService()->addRegionSamplingListener(rect, + stopLayerHandle, + listener); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::removeRegionSamplingListener( const sp<IRegionSamplingListener>& listener) { - return ComposerService::getComposerService()->removeRegionSamplingListener(listener); + binder::Status status = + ComposerServiceAIDL::getComposerService()->removeRegionSamplingListener(listener); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) { - return ComposerService::getComposerService()->addFpsListener(taskId, listener); + binder::Status status = + ComposerServiceAIDL::getComposerService()->addFpsListener(taskId, listener); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) { - return ComposerService::getComposerService()->removeFpsListener(listener); + binder::Status status = ComposerServiceAIDL::getComposerService()->removeFpsListener(listener); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::addTunnelModeEnabledListener( const sp<gui::ITunnelModeEnabledListener>& listener) { - return ComposerService::getComposerService()->addTunnelModeEnabledListener(listener); + binder::Status status = + ComposerServiceAIDL::getComposerService()->addTunnelModeEnabledListener(listener); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::removeTunnelModeEnabledListener( const sp<gui::ITunnelModeEnabledListener>& listener) { - return ComposerService::getComposerService()->removeTunnelModeEnabledListener(listener); + binder::Status status = + ComposerServiceAIDL::getComposerService()->removeTunnelModeEnabledListener(listener); + return statusTFromBinderStatus(status); } bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) { @@ -2392,7 +2711,7 @@ status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayT binder::Status status = ComposerServiceAIDL::getComposerService()->setDisplayBrightness(displayToken, brightness); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::addHdrLayerInfoListener( @@ -2400,7 +2719,7 @@ status_t SurfaceComposerClient::addHdrLayerInfoListener( binder::Status status = ComposerServiceAIDL::getComposerService()->addHdrLayerInfoListener(displayToken, listener); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::removeHdrLayerInfoListener( @@ -2408,45 +2727,79 @@ status_t SurfaceComposerClient::removeHdrLayerInfoListener( binder::Status status = ComposerServiceAIDL::getComposerService()->removeHdrLayerInfoListener(displayToken, listener); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { binder::Status status = ComposerServiceAIDL::getComposerService()->notifyPowerBoost(boostId); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) { - return ComposerService::getComposerService()->setGlobalShadowSettings(ambientColor, spotColor, - lightPosY, lightPosZ, - lightRadius); + gui::Color ambientColorG, spotColorG; + ambientColorG.r = ambientColor.r; + ambientColorG.g = ambientColor.g; + ambientColorG.b = ambientColor.b; + ambientColorG.a = ambientColor.a; + spotColorG.r = spotColor.r; + spotColorG.g = spotColor.g; + spotColorG.b = spotColor.b; + spotColorG.a = spotColor.a; + binder::Status status = + ComposerServiceAIDL::getComposerService()->setGlobalShadowSettings(ambientColorG, + spotColorG, + lightPosY, lightPosZ, + lightRadius); + return statusTFromBinderStatus(status); } std::optional<DisplayDecorationSupport> SurfaceComposerClient::getDisplayDecorationSupport( const sp<IBinder>& displayToken) { + std::optional<gui::DisplayDecorationSupport> gsupport; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getDisplayDecorationSupport(displayToken, + &gsupport); std::optional<DisplayDecorationSupport> support; - ComposerService::getComposerService()->getDisplayDecorationSupport(displayToken, &support); + if (status.isOk() && gsupport.has_value()) { + support.emplace(DisplayDecorationSupport{ + .format = + static_cast<aidl::android::hardware::graphics::common::PixelFormat>( + gsupport->format), + .alphaInterpretation = + static_cast<aidl::android::hardware::graphics::common::AlphaInterpretation>( + gsupport->alphaInterpretation) + }); + } return support; } -int SurfaceComposerClient::getGPUContextPriority() { - return ComposerService::getComposerService()->getGPUContextPriority(); +int SurfaceComposerClient::getGpuContextPriority() { + int priority; + binder::Status status = + ComposerServiceAIDL::getComposerService()->getGpuContextPriority(&priority); + if (!status.isOk()) { + status_t err = statusTFromBinderStatus(status); + ALOGE("getGpuContextPriority failed to read data: %s (%d)", strerror(-err), err); + return 0; + } + return priority; } status_t SurfaceComposerClient::addWindowInfosListener( const sp<WindowInfosListener>& windowInfosListener, std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo) { return WindowInfosListenerReporter::getInstance() - ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService(), + ->addWindowInfosListener(windowInfosListener, ComposerServiceAIDL::getComposerService(), outInitialInfo); } status_t SurfaceComposerClient::removeWindowInfosListener( const sp<WindowInfosListener>& windowInfosListener) { return WindowInfosListenerReporter::getInstance() - ->removeWindowInfosListener(windowInfosListener, ComposerService::getComposerService()); + ->removeWindowInfosListener(windowInfosListener, + ComposerServiceAIDL::getComposerService()); } // ---------------------------------------------------------------------------- @@ -2457,7 +2810,7 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, if (s == nullptr) return NO_INIT; binder::Status status = s->captureDisplay(captureArgs, captureListener); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t ScreenshotClient::captureDisplay(DisplayId displayId, @@ -2466,7 +2819,7 @@ status_t ScreenshotClient::captureDisplay(DisplayId displayId, if (s == nullptr) return NO_INIT; binder::Status status = s->captureDisplayById(displayId.value, captureListener); - return status.transactionError(); + return statusTFromBinderStatus(status); } status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, @@ -2475,7 +2828,7 @@ status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, if (s == nullptr) return NO_INIT; binder::Status status = s->captureLayers(captureArgs, captureListener); - return status.transactionError(); + return statusTFromBinderStatus(status); } // --------------------------------------------------------------------------------- @@ -2504,7 +2857,11 @@ void ReleaseCallbackThread::threadMain() { while (!callbackInfos.empty()) { auto [callbackId, releaseFence] = callbackInfos.front(); - listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX); + std::optional<os::ParcelFileDescriptor> fenceFd; + if (releaseFence != Fence::NO_FENCE) { + fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get()))); + } + listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX); callbackInfos.pop(); } diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 654fb336fe..7aee882422 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -49,13 +49,12 @@ namespace android { // ============================================================================ SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, int32_t layerId, - uint32_t w, uint32_t h, PixelFormat format, uint32_t transform, - uint32_t flags) + int32_t layerId, const std::string& name, uint32_t w, uint32_t h, + PixelFormat format, uint32_t transform, uint32_t flags) : mClient(client), mHandle(handle), - mGraphicBufferProducer(gbp), mLayerId(layerId), + mName(name), mTransformHint(transform), mWidth(w), mHeight(h), @@ -65,9 +64,9 @@ SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { mClient = other->mClient; mHandle = other->mHandle; - mGraphicBufferProducer = other->mGraphicBufferProducer; mTransformHint = other->mTransformHint; mLayerId = other->mLayerId; + mName = other->mName; mWidth = other->mWidth; mHeight = other->mHeight; mFormat = other->mFormat; @@ -165,11 +164,11 @@ sp<Surface> SurfaceControl::createSurface() void SurfaceControl::updateDefaultBufferSize(uint32_t width, uint32_t height) { Mutex::Autolock _l(mLock); - mWidth = width; mHeight = height; + mWidth = width; + mHeight = height; if (mBbq) { mBbq->update(mBbqChild, width, height, mFormat); } - } sp<IBinder> SurfaceControl::getLayerStateHandle() const @@ -188,6 +187,10 @@ int32_t SurfaceControl::getLayerId() const { return mLayerId; } +const std::string& SurfaceControl::getName() const { + return mName; +} + sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() { getSurface(); @@ -215,6 +218,7 @@ status_t SurfaceControl::writeToParcel(Parcel& parcel) { SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient())); SAFE_PARCEL(parcel.writeStrongBinder, mHandle); SAFE_PARCEL(parcel.writeInt32, mLayerId); + SAFE_PARCEL(parcel.writeUtf8AsUtf16, mName); SAFE_PARCEL(parcel.writeUint32, mTransformHint); SAFE_PARCEL(parcel.writeUint32, mWidth); SAFE_PARCEL(parcel.writeUint32, mHeight); @@ -228,6 +232,7 @@ status_t SurfaceControl::readFromParcel(const Parcel& parcel, sp<IBinder> client; sp<IBinder> handle; int32_t layerId; + std::string layerName; uint32_t transformHint; uint32_t width; uint32_t height; @@ -236,18 +241,17 @@ status_t SurfaceControl::readFromParcel(const Parcel& parcel, SAFE_PARCEL(parcel.readStrongBinder, &client); SAFE_PARCEL(parcel.readStrongBinder, &handle); SAFE_PARCEL(parcel.readInt32, &layerId); + SAFE_PARCEL(parcel.readUtf8FromUtf16, &layerName); SAFE_PARCEL(parcel.readUint32, &transformHint); SAFE_PARCEL(parcel.readUint32, &width); SAFE_PARCEL(parcel.readUint32, &height); SAFE_PARCEL(parcel.readUint32, &format); // We aren't the original owner of the surface. - *outSurfaceControl = - new SurfaceControl(new SurfaceComposerClient( - interface_cast<ISurfaceComposerClient>(client)), - handle.get(), nullptr, layerId, - width, height, format, - transformHint); + *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient( + interface_cast<ISurfaceComposerClient>(client)), + handle.get(), layerId, layerName, width, height, format, + transformHint); return NO_ERROR; } diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index 1a8fc1a00a..2d863c2585 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -36,8 +36,12 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), mHasFenceSync(false), mHasWaitSync(false) { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - // This can only be called after EGL has been initialized; otherwise the - // check below will abort. + // eglQueryString can only be called after EGL has been initialized; + // otherwise the check below will abort. If RenderEngine is using SkiaVk, + // EGL will not have been initialized. There's no problem with initializing + // it again here (it is ref counted), and then terminating it later. + EGLBoolean initialized = eglInitialize(dpy, nullptr, nullptr); + LOG_ALWAYS_FATAL_IF(!initialized, "eglInitialize failed"); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { @@ -63,6 +67,8 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), mString.append(" EGL_KHR_wait_sync"); } mString.append("]"); + // Terminate EGL to match the eglInitialize above + eglTerminate(dpy); } bool SyncFeatures::useNativeFenceSync() const { diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp deleted file mode 100644 index eedc3df009..0000000000 --- a/libs/gui/TransactionTracing.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2020 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/TransactionTracing.h" -#include "gui/ISurfaceComposer.h" - -#include <private/gui/ComposerService.h> - -namespace android { - -sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr; -std::mutex TransactionTraceListener::sMutex; - -TransactionTraceListener::TransactionTraceListener() {} - -sp<TransactionTraceListener> TransactionTraceListener::getInstance() { - const std::lock_guard<std::mutex> lock(sMutex); - - if (sInstance == nullptr) { - sInstance = new TransactionTraceListener; - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sf->addTransactionTraceListener(sInstance); - } - - return sInstance; -} - -binder::Status TransactionTraceListener::onToggled(bool enabled) { - ALOGD("TransactionTraceListener: onToggled listener called"); - mTracingEnabled = enabled; - - return binder::Status::ok(); -} - -bool TransactionTraceListener::isTracingEnabled() { - return mTracingEnabled; -} - -} // namespace android
\ No newline at end of file diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 4e966d1393..804ce4fac0 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -76,7 +76,7 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags && info.isClone == isClone; + info.layoutParamsFlags == layoutParamsFlags; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -124,8 +124,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken) ?: - parcel->writeBool(isClone); + parcel->writeStrongBinder(windowToken); // clang-format on return status; } @@ -176,8 +175,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->read(touchableRegion) ?: parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: - parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readBool(&isClone); + parcel->readNullableStrongBinder(&windowToken); // clang-format on if (status != OK) { diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index cfc7dbc463..01e865da6a 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -14,7 +14,8 @@ * limitations under the License. */ -#include <gui/ISurfaceComposer.h> +#include <android/gui/ISurfaceComposer.h> +#include <gui/AidlStatusUtil.h> #include <gui/WindowInfosListenerReporter.h> namespace android { @@ -23,6 +24,7 @@ using gui::DisplayInfo; using gui::IWindowInfosReportedListener; using gui::WindowInfo; using gui::WindowInfosListener; +using gui::aidl_utils::statusTFromBinderStatus; sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() { static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter; @@ -31,13 +33,14 @@ sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() { status_t WindowInfosListenerReporter::addWindowInfosListener( const sp<WindowInfosListener>& windowInfosListener, - const sp<ISurfaceComposer>& surfaceComposer, + const sp<gui::ISurfaceComposer>& surfaceComposer, std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo) { status_t status = OK; { std::scoped_lock lock(mListenersMutex); if (mWindowInfosListeners.empty()) { - status = surfaceComposer->addWindowInfosListener(this); + binder::Status s = surfaceComposer->addWindowInfosListener(this); + status = statusTFromBinderStatus(s); } if (status == OK) { @@ -55,12 +58,13 @@ status_t WindowInfosListenerReporter::addWindowInfosListener( status_t WindowInfosListenerReporter::removeWindowInfosListener( const sp<WindowInfosListener>& windowInfosListener, - const sp<ISurfaceComposer>& surfaceComposer) { + const sp<gui::ISurfaceComposer>& surfaceComposer) { status_t status = OK; { std::scoped_lock lock(mListenersMutex); if (mWindowInfosListeners.size() == 1) { - status = surfaceComposer->removeWindowInfosListener(this); + binder::Status s = surfaceComposer->removeWindowInfosListener(this); + status = statusTFromBinderStatus(s); // Clear the last stored state since we're disabling updates and don't want to hold // stale values mLastWindowInfos.clear(); @@ -78,7 +82,8 @@ status_t WindowInfosListenerReporter::removeWindowInfosListener( binder::Status WindowInfosListenerReporter::onWindowInfosChanged( const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos, const sp<IWindowInfosReportedListener>& windowInfosReportedListener) { - std::unordered_set<sp<WindowInfosListener>, SpHash<WindowInfosListener>> windowInfosListeners; + std::unordered_set<sp<WindowInfosListener>, gui::SpHash<WindowInfosListener>> + windowInfosListeners; { std::scoped_lock lock(mListenersMutex); @@ -101,7 +106,7 @@ binder::Status WindowInfosListenerReporter::onWindowInfosChanged( return binder::Status::ok(); } -void WindowInfosListenerReporter::reconnect(const sp<ISurfaceComposer>& composerService) { +void WindowInfosListenerReporter::reconnect(const sp<gui::ISurfaceComposer>& composerService) { std::scoped_lock lock(mListenersMutex); if (!mWindowInfosListeners.empty()) { composerService->addWindowInfosListener(this); diff --git a/libs/gui/aidl/android/gui/Rect.aidl b/libs/gui/aidl/android/gui/ARect.aidl index 1b13761392..5785907a9c 100644 --- a/libs/gui/aidl/android/gui/Rect.aidl +++ b/libs/gui/aidl/android/gui/ARect.aidl @@ -20,7 +20,7 @@ package android.gui; // TODO(b/221473398): // use hardware/interfaces/graphics/common/aidl/android/hardware/graphics/common/Rect.aidl /** @hide */ -parcelable Rect { +parcelable ARect { /// Minimum X coordinate of the rectangle. int left; diff --git a/libs/gui/aidl/android/gui/Color.aidl b/libs/gui/aidl/android/gui/Color.aidl new file mode 100644 index 0000000000..12af066562 --- /dev/null +++ b/libs/gui/aidl/android/gui/Color.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable Color { + float r; + float g; + float b; + float a; +} diff --git a/libs/gui/aidl/android/gui/CompositionPreference.aidl b/libs/gui/aidl/android/gui/CompositionPreference.aidl new file mode 100644 index 0000000000..b615824a7d --- /dev/null +++ b/libs/gui/aidl/android/gui/CompositionPreference.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable CompositionPreference { + int /*ui::Dataspace*/ defaultDataspace; + int /*ui::PixelFormat*/ defaultPixelFormat; + int /*ui::Dataspace*/ wideColorGamutDataspace; + int /*ui::PixelFormat*/ wideColorGamutPixelFormat; +} diff --git a/libs/gui/aidl/android/gui/ContentSamplingAttributes.aidl b/libs/gui/aidl/android/gui/ContentSamplingAttributes.aidl new file mode 100644 index 0000000000..5d913b1da6 --- /dev/null +++ b/libs/gui/aidl/android/gui/ContentSamplingAttributes.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable ContentSamplingAttributes { + int /*ui::PixelFormat*/ format; + int /*ui::Dataspace*/ dataspace; + byte componentMask; +} diff --git a/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl new file mode 100644 index 0000000000..eea12dc75d --- /dev/null +++ b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable CreateSurfaceResult { + IBinder handle; + int layerId; + String layerName; + int transformHint; +} diff --git a/libs/gui/aidl/android/gui/DeviceProductInfo.aidl b/libs/gui/aidl/android/gui/DeviceProductInfo.aidl new file mode 100644 index 0000000000..98404cf0fd --- /dev/null +++ b/libs/gui/aidl/android/gui/DeviceProductInfo.aidl @@ -0,0 +1,58 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +// Product-specific information about the display or the directly connected device on the +// display chain. For example, if the display is transitively connected, this field may contain +// product information about the intermediate device. + +/** @hide */ +parcelable DeviceProductInfo { + parcelable ModelYear { + int year; + } + + parcelable ManufactureYear { + ModelYear modelYear; + } + + parcelable ManufactureWeekAndYear { + ManufactureYear manufactureYear; + + // 1-base week number. Week numbering may not be consistent between manufacturers. + int week; + } + + union ManufactureOrModelDate { + ModelYear modelYear; + ManufactureYear manufactureYear; + ManufactureWeekAndYear manufactureWeekAndYear; + } + + // Display name. + @utf8InCpp String name; + + // NULL-terminated Manufacturer plug and play ID. + byte[] manufacturerPnpId; + + // Manufacturer product ID. + @utf8InCpp String productId; + + ManufactureOrModelDate manufactureOrModelDate; + + byte[] relativeAddress; +} diff --git a/libs/gui/aidl/android/gui/DisplayConnectionType.aidl b/libs/gui/aidl/android/gui/DisplayConnectionType.aidl new file mode 100644 index 0000000000..72c4ede7ac --- /dev/null +++ b/libs/gui/aidl/android/gui/DisplayConnectionType.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +@Backing(type="int") +enum DisplayConnectionType { + Internal = 0, + External = 1 +} diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/libs/gui/aidl/android/gui/DisplayDecorationSupport.aidl index 5aeb21f6b4..023049657b 100644 --- a/services/inputflinger/tests/IInputFlingerQuery.aidl +++ b/libs/gui/aidl/android/gui/DisplayDecorationSupport.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020, The Android Open Source Project + * Copyright (c) 2022, 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. @@ -14,14 +14,12 @@ * limitations under the License. */ -import android.InputChannel; -import android.gui.FocusRequest; -import android.gui.WindowInfo; +package android.gui; +// TODO(b/222607970): +// remove this aidl and use android.hardware.graphics.common.DisplayDecorationSupport /** @hide */ -interface IInputFlingerQuery -{ - /* Test interfaces */ - void getInputChannels(out InputChannel[] channels); - void resetInputManager(); +parcelable DisplayDecorationSupport { + int format; + int alphaInterpretation; } diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl new file mode 100644 index 0000000000..ce30426cb5 --- /dev/null +++ b/libs/gui/aidl/android/gui/DisplayMode.aidl @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.Size; + +// Mode supported by physical display. +// Make sure to sync with libui DisplayMode.h + +/** @hide */ +parcelable DisplayMode { + int id; + Size resolution; + float xDpi = 0.0f; + float yDpi = 0.0f; + int[] supportedHdrTypes; + + float refreshRate = 0.0f; + long appVsyncOffset = 0; + long sfVsyncOffset = 0; + long presentationDeadline = 0; + int group = -1; +} diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl new file mode 100644 index 0000000000..af138c7539 --- /dev/null +++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl @@ -0,0 +1,75 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable DisplayModeSpecs { + /** + * Defines the refresh rates ranges that should be used by SF. + */ + parcelable RefreshRateRanges { + /** + * Defines a range of refresh rates. + */ + parcelable RefreshRateRange { + float min; + float max; + } + + /** + * The range of refresh rates that the display should run at. + */ + RefreshRateRange physical; + + /** + * The range of refresh rates that apps should render at. + */ + RefreshRateRange render; + } + + /** + * Base mode ID. This is what system defaults to for all other settings, or + * if the refresh rate range is not available. + */ + int defaultMode; + + /** + * If true this will allow switching between modes in different display configuration + * groups. This way the user may see visual interruptions when the display mode changes. + */ + + boolean allowGroupSwitching; + + /** + * The primary physical and render refresh rate ranges represent DisplayManager's general + * guidance on the display modes SurfaceFlinger will consider when switching refresh + * rates and scheduling the frame rate. Unless SurfaceFlinger has a specific reason to do + * otherwise, it will stay within this range. + */ + RefreshRateRanges primaryRanges; + + /** + * The app request physical and render refresh rate ranges allow SurfaceFlinger to consider + * more display modes when switching refresh rates. Although SurfaceFlinger will + * generally stay within the primary range, specific considerations, such as layer frame + * rate settings specified via the setFrameRate() API, may cause SurfaceFlinger to go + * outside the primary range. SurfaceFlinger never goes outside the app request range. + * The app request range will be greater than or equal to the primary refresh rate range, + * never smaller. + */ + RefreshRateRanges appRequestRanges; +} diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/gui/aidl/android/gui/DisplayPrimaries.aidl index 6929a6cb49..dbf668c629 100644 --- a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl +++ b/libs/gui/aidl/android/gui/DisplayPrimaries.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2022 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. @@ -14,15 +14,20 @@ * limitations under the License. */ -package android.content.pm; +package android.gui; -import android.content.pm.PackageChangeEvent; +// copied from libui ConfigStoreTypes.h -/** - * This is a non-blocking notification when a package has changed. - * - * @hide - */ -oneway interface IPackageChangeObserver { - void onPackageChanged(in PackageChangeEvent event); +/** @hide */ +parcelable DisplayPrimaries { + parcelable CieXyz { + float X; + float Y; + float Z; + } + + CieXyz red; + CieXyz green; + CieXyz blue; + CieXyz white; } diff --git a/libs/gui/aidl/android/gui/DisplayedFrameStats.aidl b/libs/gui/aidl/android/gui/DisplayedFrameStats.aidl new file mode 100644 index 0000000000..f4b6dadc49 --- /dev/null +++ b/libs/gui/aidl/android/gui/DisplayedFrameStats.aidl @@ -0,0 +1,40 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable DisplayedFrameStats { + /* The number of frames represented by this sample. */ + long numFrames = 0; + + /* A histogram counting how many times a pixel of a given value was displayed onscreen for + * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets + * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that + * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels + * were displayed onscreen in range 0x40->0x7F, etc. + */ + long[] component_0_sample; + + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */ + long[] component_1_sample; + + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */ + long[] component_2_sample; + + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */ + long[] component_3_sample; +} diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl new file mode 100644 index 0000000000..3114929e86 --- /dev/null +++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl @@ -0,0 +1,46 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.DisplayMode; +import android.gui.HdrCapabilities; + +// Information about a physical display which may change on hotplug reconnect. +// Make sure to sync with libui DynamicDisplayInfo.h + +/** @hide */ +parcelable DynamicDisplayInfo { + List<DisplayMode> supportedDisplayModes; + + int activeDisplayModeId; + float renderFrameRate; + + int[] supportedColorModes; + int activeColorMode; + HdrCapabilities hdrCapabilities; + + // True if the display reports support for HDMI 2.1 Auto Low Latency Mode. + // For more information, see the HDMI 2.1 specification. + boolean autoLowLatencyModeSupported; + + // True if the display reports support for Game Content Type. + // For more information, see the HDMI 1.4 specification. + boolean gameContentTypeSupported; + + // The boot display mode preferred by the implementation. + int preferredBootDisplayMode; +} diff --git a/libs/gui/aidl/android/gui/FrameEvent.aidl b/libs/gui/aidl/android/gui/FrameEvent.aidl new file mode 100644 index 0000000000..aaabdb5b54 --- /dev/null +++ b/libs/gui/aidl/android/gui/FrameEvent.aidl @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +// Identifiers for all the events that may be recorded or reported. + +/** @hide */ +@Backing(type="int") +enum FrameEvent { + POSTED = 0, + REQUESTED_PRESENT = 1, + LATCH = 2, + ACQUIRE = 3, + FIRST_REFRESH_START = 4, + LAST_REFRESH_START = 5, + GPU_COMPOSITION_DONE = 6, + DISPLAY_PRESENT = 7, + DEQUEUE_READY = 8, + RELEASE = 9, + EVENT_COUNT = 10 // Not an actual event. +} diff --git a/libs/gui/aidl/android/gui/FrameStats.aidl b/libs/gui/aidl/android/gui/FrameStats.aidl new file mode 100644 index 0000000000..a145e74b11 --- /dev/null +++ b/libs/gui/aidl/android/gui/FrameStats.aidl @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +// Make sure to sync with libui FrameStats.h + +/** @hide */ +parcelable FrameStats { + /* + * Approximate refresh time, in nanoseconds. + */ + long refreshPeriodNano; + + /* + * The times in nanoseconds for when the frame contents were posted by the producer (e.g. + * the application). They are either explicitly set or defaulted to the time when + * Surface::queueBuffer() was called. + */ + long[] desiredPresentTimesNano; + + /* + * The times in milliseconds for when the frame contents were presented on the screen. + */ + long[] actualPresentTimesNano; + + /* + * The times in nanoseconds for when the frame contents were ready to be presented. Note that + * a frame can be posted and still it contents being rendered asynchronously in GL. In such a + * case these are the times when the frame contents were completely rendered (i.e. their fences + * signaled). + */ + long[] frameReadyTimesNano; +} diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl index 255ce568d2..6ffe466f20 100644 --- a/libs/gui/include/gui/FrameTimelineInfo.h +++ b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Android Open Source Project + * Copyright 2022 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. @@ -14,36 +14,23 @@ * limitations under the License. */ -#pragma once +package android.gui; -#include <stdint.h> - -#include <binder/Parcel.h> - -namespace android { - -struct FrameTimelineInfo { +/** @hide */ +parcelable FrameTimelineInfo { // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java - static constexpr int64_t INVALID_VSYNC_ID = -1; + const long INVALID_VSYNC_ID = -1; // The vsync id that was used to start the transaction - int64_t vsyncId = INVALID_VSYNC_ID; + long vsyncId = INVALID_VSYNC_ID; // The id of the input event that caused this buffer // Default is android::os::IInputConstants::INVALID_INPUT_EVENT_ID = 0 // We copy the value of the input event ID instead of including the header, because libgui // header libraries containing FrameTimelineInfo must be available to vendors, but libinput is // not directly vendor available. - int32_t inputEventId = 0; + int inputEventId = 0; // The current time in nanoseconds the application started to render the frame. - int64_t startTimeNanos = 0; - - status_t write(Parcel& output) const; - status_t read(const Parcel& input); - - void merge(const FrameTimelineInfo& other); - void clear(); -}; - -} // namespace android + long startTimeNanos = 0; +} diff --git a/libs/gui/aidl/android/gui/HdrCapabilities.aidl b/libs/gui/aidl/android/gui/HdrCapabilities.aidl new file mode 100644 index 0000000000..9d06da9f27 --- /dev/null +++ b/libs/gui/aidl/android/gui/HdrCapabilities.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +// Make sure to sync with libui HdrCapabilities.h + +/** @hide */ +parcelable HdrCapabilities { + int[] supportedHdrTypes; + float maxLuminance; + float maxAverageLuminance; + float minLuminance; +} diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index b31b37bada..488a1486cc 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -16,39 +16,97 @@ package android.gui; -import android.gui.DisplayCaptureArgs; +import android.gui.Color; +import android.gui.CompositionPreference; +import android.gui.ContentSamplingAttributes; import android.gui.DisplayBrightness; +import android.gui.DisplayCaptureArgs; +import android.gui.DisplayDecorationSupport; +import android.gui.DisplayedFrameStats; +import android.gui.DisplayModeSpecs; +import android.gui.DisplayPrimaries; import android.gui.DisplayState; import android.gui.DisplayStatInfo; +import android.gui.DynamicDisplayInfo; +import android.gui.FrameEvent; +import android.gui.FrameStats; +import android.gui.IDisplayEventConnection; +import android.gui.IFpsListener; import android.gui.IHdrLayerInfoListener; -import android.gui.LayerCaptureArgs; +import android.gui.IRegionSamplingListener; import android.gui.IScreenCaptureListener; +import android.gui.ISurfaceComposerClient; +import android.gui.ITunnelModeEnabledListener; +import android.gui.IWindowInfosListener; +import android.gui.LayerCaptureArgs; +import android.gui.LayerDebugInfo; +import android.gui.OverlayProperties; +import android.gui.PullAtomData; +import android.gui.ARect; +import android.gui.StaticDisplayInfo; /** @hide */ interface ISurfaceComposer { - /* create a virtual display + enum VsyncSource { + eVsyncSourceApp = 0, + eVsyncSourceSurfaceFlinger = 1 + } + + enum EventRegistration { + modeChanged = 1 << 0, + frameRateOverride = 1 << 1, + } + + /** + * Signal that we're done booting. + * Requires ACCESS_SURFACE_FLINGER permission + */ + // Note this must be the 1st method, so IBinder::FIRST_CALL_TRANSACTION + // is assigned, as it is called from Java by ActivityManagerService. + void bootFinished(); + + /** + * Create a display event connection + */ + @nullable IDisplayEventConnection createDisplayEventConnection(VsyncSource vsyncSource, + EventRegistration eventRegistration); + + /** + * Create a connection with SurfaceFlinger. + */ + @nullable ISurfaceComposerClient createConnection(); + + /** + * Create a virtual display * requires ACCESS_SURFACE_FLINGER permission. */ @nullable IBinder createDisplay(@utf8InCpp String displayName, boolean secure); - /* destroy a virtual display + /** + * Destroy a virtual display * requires ACCESS_SURFACE_FLINGER permission. */ void destroyDisplay(IBinder display); - /* get stable IDs for connected physical displays. + /** + * Get stable IDs for connected physical displays. */ long[] getPhysicalDisplayIds(); - long getPrimaryPhysicalDisplayId(); - - /* get token for a physical display given its stable ID obtained via getPhysicalDisplayIds or a - * DisplayEventReceiver hotplug event. + /** + * Get token for a physical display given its stable ID obtained via getPhysicalDisplayIds or + * a DisplayEventReceiver hotplug event. */ @nullable IBinder getPhysicalDisplayToken(long displayId); - /* set display power mode. depending on the mode, it can either trigger + /** + * Returns the frame timestamps supported by SurfaceFlinger. + */ + FrameEvent[] getSupportedFrameTimestamps(); + + /** + * Set display power mode. depending on the mode, it can either trigger * screen on, off or low power mode and wait for it to complete. * requires ACCESS_SURFACE_FLINGER permission. */ @@ -60,12 +118,33 @@ interface ISurfaceComposer { * video frames */ DisplayStatInfo getDisplayStats(@nullable IBinder display); - /** + /** * Get transactional state of given display. */ DisplayState getDisplayState(IBinder display); /** + * Gets immutable information about given physical display. + */ + StaticDisplayInfo getStaticDisplayInfo(long displayId); + + /** + * Gets dynamic information about given physical display. + */ + DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId); + + DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display); + + DisplayPrimaries getDisplayNativePrimaries(IBinder display); + + void setActiveColorMode(IBinder display, int colorMode); + + /** + * Sets the user-preferred display mode that a device should boot in. + */ + void setBootDisplayMode(IBinder display, int displayModeId); + + /** * Clears the user-preferred display mode. The device should now boot in system preferred * display mode. */ @@ -110,7 +189,9 @@ interface ISurfaceComposer { * match the size of the output buffer. */ void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener); + void captureDisplayById(long displayId, IScreenCaptureListener listener); + /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there @@ -118,13 +199,143 @@ interface ISurfaceComposer { */ void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); - /* + /** + * Clears the frame statistics for animations. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + void clearAnimationFrameStats(); + + /** + * Gets the frame statistics for animations. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + FrameStats getAnimationFrameStats(); + + /** + * Overrides the supported HDR modes for the given display device. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + void overrideHdrTypes(IBinder display, in int[] hdrTypes); + + /** + * Pulls surfaceflinger atoms global stats and layer stats to pipe to statsd. + * + * Requires the calling uid be from system server. + */ + PullAtomData onPullAtom(int atomId); + + /** + * Gets the list of active layers in Z order for debugging purposes + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + List<LayerDebugInfo> getLayerDebugInfo(); + + boolean getColorManagement(); + + /** + * Gets the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + * + */ + CompositionPreference getCompositionPreference(); + + /** + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + ContentSamplingAttributes getDisplayedContentSamplingAttributes(IBinder display); + + /** + * Turns on the color sampling engine on the display. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + void setDisplayContentSamplingEnabled(IBinder display, boolean enable, byte componentMask, long maxFrames); + + /** + * Returns statistics on the color profile of the last frame displayed for a given display + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + DisplayedFrameStats getDisplayedContentSample(IBinder display, long maxFrames, long timestamp); + + /** + * Gets whether SurfaceFlinger can support protected content in GPU composition. + */ + boolean getProtectedContentSupport(); + + /** * Queries whether the given display is a wide color display. * Requires the ACCESS_SURFACE_FLINGER permission. */ boolean isWideColorDisplay(IBinder token); - /* + /** + * Registers a listener to stream median luma updates from SurfaceFlinger. + * + * The sampling area is bounded by both samplingArea and the given stopLayerHandle + * (i.e., only layers behind the stop layer will be captured and sampled). + * + * Multiple listeners may be provided so long as they have independent listeners. + * If multiple listeners are provided, the effective sampling region for each listener will + * be bounded by whichever stop layer has a lower Z value. + * + * Requires the same permissions as captureLayers and captureScreen. + */ + void addRegionSamplingListener(in ARect samplingArea, @nullable IBinder stopLayerHandle, IRegionSamplingListener listener); + + /** + * Removes a listener that was streaming median luma updates from SurfaceFlinger. + */ + void removeRegionSamplingListener(IRegionSamplingListener listener); + + /** + * Registers a listener that streams fps updates from SurfaceFlinger. + * + * The listener will stream fps updates for the layer tree rooted at the layer denoted by the + * task ID, i.e., the layer must have the task ID as part of its layer metadata with key + * METADATA_TASK_ID. If there is no such layer, then no fps is expected to be reported. + * + * Multiple listeners may be supported. + * + * Requires the READ_FRAME_BUFFER permission. + */ + void addFpsListener(int taskId, IFpsListener listener); + + /** + * Removes a listener that was streaming fps updates from SurfaceFlinger. + */ + void removeFpsListener(IFpsListener listener); + + /** + * Registers a listener to receive tunnel mode enabled updates from SurfaceFlinger. + * + * Requires ACCESS_SURFACE_FLINGER permission. + */ + void addTunnelModeEnabledListener(ITunnelModeEnabledListener listener); + + /** + * Removes a listener that was receiving tunnel mode enabled updates from SurfaceFlinger. + * + * Requires ACCESS_SURFACE_FLINGER permission. + */ + void removeTunnelModeEnabledListener(ITunnelModeEnabledListener listener); + + /** + * Sets the refresh rate boundaries for the display. + * + * @see DisplayModeSpecs.aidl for details. + */ + void setDesiredDisplayModeSpecs(IBinder displayToken, in DisplayModeSpecs specs); + + DisplayModeSpecs getDesiredDisplayModeSpecs(IBinder displayToken); + + /** * Gets whether brightness operations are supported on a display. * * displayToken @@ -138,7 +349,7 @@ interface ISurfaceComposer { */ boolean getDisplayBrightnessSupport(IBinder displayToken); - /* + /** * Sets the brightness of a display. * * displayToken @@ -153,7 +364,7 @@ interface ISurfaceComposer { */ void setDisplayBrightness(IBinder displayToken, in DisplayBrightness brightness); - /* + /** * Adds a listener that receives HDR layer information. This is used in combination * with setDisplayBrightness to adjust the display brightness depending on factors such * as whether or not HDR is in use. @@ -162,7 +373,7 @@ interface ISurfaceComposer { */ void addHdrLayerInfoListener(IBinder displayToken, IHdrLayerInfoListener listener); - /* + /** * Removes a listener that was added with addHdrLayerInfoListener. * * Returns NO_ERROR upon success, NAME_NOT_FOUND if the display is invalid, and BAD_VALUE if @@ -171,7 +382,7 @@ interface ISurfaceComposer { */ void removeHdrLayerInfoListener(IBinder displayToken, IHdrLayerInfoListener listener); - /* + /** * Sends a power boost to the composer. This function is asynchronous. * * boostId @@ -179,5 +390,75 @@ interface ISurfaceComposer { * * Returns NO_ERROR upon success. */ - void notifyPowerBoost(int boostId); + oneway void notifyPowerBoost(int boostId); + + /* + * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows + * material design guidelines. + * + * ambientColor + * Color to the ambient shadow. The alpha is premultiplied. + * + * spotColor + * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow + * depends on the light position. + * + * lightPosY/lightPosZ + * Position of the light used to cast the spot shadow. The X value is always the display + * width / 2. + * + * lightRadius + * Radius of the light casting the shadow. + */ + oneway void setGlobalShadowSettings(in Color ambientColor, in Color spotColor, float lightPosY, float lightPosZ, float lightRadius); + + /** + * Gets whether a display supports DISPLAY_DECORATION layers. + * + * displayToken + * The token of the display. + * outSupport + * An output parameter for whether/how the display supports + * DISPLAY_DECORATION layers. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display is invalid, or + * BAD_VALUE if the output parameter is invalid. + */ + @nullable DisplayDecorationSupport getDisplayDecorationSupport(IBinder displayToken); + + /** + * Set the override frame rate for a specified uid by GameManagerService. + * Passing the frame rate and uid to SurfaceFlinger to update the override mapping + * in the scheduler. + */ + void setOverrideFrameRate(int uid, float frameRate); + + /** + * Gets priority of the RenderEngine in SurfaceFlinger. + */ + int getGpuContextPriority(); + + /** + * Gets the number of buffers SurfaceFlinger would need acquire. This number + * would be propagated to the client via MIN_UNDEQUEUED_BUFFERS so that the + * client could allocate enough buffers to match SF expectations of the + * pipeline depth. SurfaceFlinger will make sure that it will give the app at + * least the time configured as the 'appDuration' before trying to latch + * the buffer. + * + * The total buffers needed for a given configuration is basically the + * numbers of vsyncs a single buffer is used across the stack. For the default + * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger + * and 1 vsync by the display. The extra buffers are calculated as the + * number of additional buffers on top of the 2 buffers already present + * in MIN_UNDEQUEUED_BUFFERS. + */ + int getMaxAcquiredBufferCount(); + + void addWindowInfosListener(IWindowInfosListener windowInfosListener); + + void removeWindowInfosListener(IWindowInfosListener windowInfosListener); + + OverlayProperties getOverlaySupport(); } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl new file mode 100644 index 0000000000..68781ce953 --- /dev/null +++ b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl @@ -0,0 +1,63 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.CreateSurfaceResult; +import android.gui.FrameStats; +import android.gui.LayerMetadata; + +/** @hide */ +interface ISurfaceComposerClient { + + // flags for createSurface() + // (keep in sync with SurfaceControl.java) + const int eHidden = 0x00000004; + const int eDestroyBackbuffer = 0x00000020; + const int eSkipScreenshot = 0x00000040; + const int eSecure = 0x00000080; + const int eNonPremultiplied = 0x00000100; + const int eOpaque = 0x00000400; + const int eProtectedByApp = 0x00000800; + const int eProtectedByDRM = 0x00001000; + const int eCursorWindow = 0x00002000; + const int eNoColorFill = 0x00004000; + + const int eFXSurfaceBufferQueue = 0x00000000; + const int eFXSurfaceEffect = 0x00020000; + const int eFXSurfaceBufferState = 0x00040000; + const int eFXSurfaceContainer = 0x00080000; + const int eFXSurfaceMask = 0x000F0000; + + /** + * Requires ACCESS_SURFACE_FLINGER permission + */ + CreateSurfaceResult createSurface(@utf8InCpp String name, int flags, @nullable IBinder parent, in LayerMetadata metadata); + + /** + * Requires ACCESS_SURFACE_FLINGER permission + */ + void clearLayerFrameStats(IBinder handle); + + /** + * Requires ACCESS_SURFACE_FLINGER permission + */ + FrameStats getLayerFrameStats(IBinder handle); + + CreateSurfaceResult mirrorSurface(IBinder mirrorFromHandle); + + CreateSurfaceResult mirrorDisplay(long displayId); +} diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl new file mode 100644 index 0000000000..dde4d38cba --- /dev/null +++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl @@ -0,0 +1,31 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.ListenerStats; +import android.gui.ReleaseCallbackId; + +/** @hide */ +oneway interface ITransactionCompletedListener { + void onTransactionCompleted(in ListenerStats stats); + + void onReleaseBuffer(in ReleaseCallbackId callbackId, + in @nullable ParcelFileDescriptor releaseFenceFd, + int currentMaxAcquiredBufferCount); + + void onTransactionQueueStalled(@utf8InCpp String name); +} diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl deleted file mode 100644 index 5cd12fdc2b..0000000000 --- a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl +++ /dev/null @@ -1,6 +0,0 @@ -package android.gui; - -/** @hide */ -interface ITransactionTraceListener { - void onToggled(boolean enabled); -}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/libs/gui/aidl/android/gui/LayerDebugInfo.aidl new file mode 100644 index 0000000000..faca980f3c --- /dev/null +++ b/libs/gui/aidl/android/gui/LayerDebugInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h"; diff --git a/libs/gui/aidl/android/gui/LayerMetadata.aidl b/libs/gui/aidl/android/gui/LayerMetadata.aidl new file mode 100644 index 0000000000..1368ac512f --- /dev/null +++ b/libs/gui/aidl/android/gui/LayerMetadata.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable LayerMetadata cpp_header "gui/LayerMetadata.h"; diff --git a/libs/gui/aidl/android/gui/ListenerStats.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl new file mode 100644 index 0000000000..63248b2bf3 --- /dev/null +++ b/libs/gui/aidl/android/gui/ListenerStats.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable ListenerStats cpp_header "gui/ListenerStats.h"; diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl new file mode 100644 index 0000000000..75cea157aa --- /dev/null +++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable OverlayProperties { + parcelable SupportedBufferCombinations { + int[] pixelFormats; + int[] dataspaces; + } + SupportedBufferCombinations[] combinations; +} diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl new file mode 100644 index 0000000000..c307cef70e --- /dev/null +++ b/libs/gui/aidl/android/gui/PullAtomData.aidl @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +parcelable PullAtomData { + byte[] data; + boolean success; +} diff --git a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl new file mode 100644 index 0000000000..c86de34de9 --- /dev/null +++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h"; diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl new file mode 100644 index 0000000000..0ccda56ef5 --- /dev/null +++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.DisplayConnectionType; +import android.gui.DeviceProductInfo; +import android.gui.Rotation; + +/** @hide */ +parcelable StaticDisplayInfo { + DisplayConnectionType connectionType = DisplayConnectionType.Internal; + float density; + boolean secure; + @nullable DeviceProductInfo deviceProductInfo; + Rotation installOrientation = Rotation.Rotation0; +} diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp new file mode 100644 index 0000000000..82e1b5ae4d --- /dev/null +++ b/libs/gui/fuzzer/Android.bp @@ -0,0 +1,136 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_defaults { + name: "libgui_fuzzer_defaults", + static_libs: [ + "android.hidl.token@1.0-utils", + "libbinder_random_parcel", + "libgui_aidl_static", + "libgui_window_info_static", + "libpdx", + "libgmock", + "libgui_mocks", + "libgmock_ndk", + "libgmock_main", + "libgtest_ndk_c++", + "libgmock_main_ndk", + "librenderengine_mocks", + "perfetto_trace_protos", + "libcompositionengine_mocks", + "perfetto_trace_protos", + ], + shared_libs: [ + "android.hardware.configstore@1.0", + "android.hardware.configstore-utils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + "android.hardware.power-V4-cpp", + "android.hidl.token@1.0", + "libSurfaceFlingerProp", + "libgui", + "libbase", + "liblog", + "libEGL", + "libGLESv2", + "libbinder", + "libcutils", + "libhidlbase", + "libinput", + "libui", + "libutils", + "libnativewindow", + "libvndksupport", + ], + header_libs: [ + "libdvr_headers", + "libui_fuzzableDataspaces_headers", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} + +cc_fuzz { + name: "libgui_surfaceComposer_fuzzer", + srcs: [ + "libgui_surfaceComposer_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} + +cc_fuzz { + name: "libgui_surfaceComposerClient_fuzzer", + srcs: [ + "libgui_surfaceComposerClient_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} + +cc_fuzz { + name: "libgui_parcelable_fuzzer", + srcs: [ + "libgui_parcelable_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} + +cc_fuzz { + name: "libgui_bufferQueue_fuzzer", + srcs: [ + "libgui_bufferQueue_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} + +cc_fuzz { + name: "libgui_consumer_fuzzer", + srcs: [ + "libgui_consumer_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} + +cc_fuzz { + name: "libgui_displayEvent_fuzzer", + srcs: [ + "libgui_displayEvent_fuzzer.cpp", + ], + defaults: [ + "libgui_fuzzer_defaults", + ], +} diff --git a/libs/gui/fuzzer/README.md b/libs/gui/fuzzer/README.md new file mode 100644 index 0000000000..96e27c989f --- /dev/null +++ b/libs/gui/fuzzer/README.md @@ -0,0 +1,219 @@ +# Fuzzers for Libgui + +## Table of contents ++ [libgui_surfaceComposer_fuzzer](#SurfaceComposer) ++ [libgui_surfaceComposerClient_fuzzer](#SurfaceComposerClient) ++ [libgui_parcelable_fuzzer](#Libgui_Parcelable) ++ [libgui_bufferQueue_fuzzer](#BufferQueue) ++ [libgui_consumer_fuzzer](#Libgui_Consumer) ++ [libgui_displayEvent_fuzzer](#LibGui_DisplayEvent) + +# <a name="libgui_surfaceComposer_fuzzer"></a> Fuzzer for SurfaceComposer + +SurfaceComposer supports the following parameters: +1. SurfaceWidth (parameter name:`width`) +2. SurfaceHeight (parameter name:`height`) +3. TransactionStateFlags (parameter name:`flags`) +4. TransformHint (parameter name:`outTransformHint`) +5. SurfacePixelFormat (parameter name:`format`) +6. LayerId (parameter name:`outLayerId`) +7. SurfaceComposerTags (parameter name:`surfaceTag`) +8. PowerBoostID (parameter name:`boostId`) +9. VsyncSource (parameter name:`vsyncSource`) +10. EventRegistrationFlags (parameter name:`eventRegistration`) +11. FrameRateCompatibility (parameter name:`frameRateCompatibility`) +12. ChangeFrameRateStrategy (parameter name:`changeFrameRateStrategy`) +13. HdrTypes (parameter name:`hdrTypes`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`surfaceTag` | 0.`BnSurfaceComposer::BOOT_FINISHED`, 1.`BnSurfaceComposer::CREATE_CONNECTION`, 2.`BnSurfaceComposer::GET_STATIC_DISPLAY_INFO`, 3.`BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION`, 4.`BnSurfaceComposer::CREATE_DISPLAY`, 5.`BnSurfaceComposer::DESTROY_DISPLAY`, 6.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN`, 7.`BnSurfaceComposer::SET_TRANSACTION_STATE`, 8.`BnSurfaceComposer::AUTHENTICATE_SURFACE`, 9.`BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS`, 10.`BnSurfaceComposer::GET_DISPLAY_STATE`, 11.`BnSurfaceComposer::CAPTURE_DISPLAY`, 12.`BnSurfaceComposer::CAPTURE_LAYERS`, 13.`BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS`, 14.`BnSurfaceComposer::GET_ANIMATION_FRAME_STATS`, 15.`BnSurfaceComposer::SET_POWER_MODE`, 16.`BnSurfaceComposer::GET_DISPLAY_STATS`, 17.`BnSurfaceComposer::SET_ACTIVE_COLOR_MODE`, 18.`BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS`, 19.`BnSurfaceComposer::INJECT_VSYNC`, 20.`BnSurfaceComposer::GET_LAYER_DEBUG_INFO`, 21.`BnSurfaceComposer::GET_COMPOSITION_PREFERENCE`, 22.`BnSurfaceComposer::GET_COLOR_MANAGEMENT`, 23.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES`, 24.`BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED`, 25.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE`, 26.`BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT`, 27.`BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY`, 28.`BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES`, 29.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS`, 30.`BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER`, 31.`BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER`, 32.`BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS`, 33.`BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS`, 34.`BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT`, 35.`BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS`, 36.`BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID`, 37.`BnSurfaceComposer::NOTIFY_POWER_BOOST`, 38.`BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS`, 39.`BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE`, 40.`BnSurfaceComposer::SET_GAME_CONTENT_TYPE`, 41.`BnSurfaceComposer::SET_FRAME_RATE`, 42.`BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN`, 43.`BnSurfaceComposer::SET_FRAME_TIMELINE_INFO`, 44.`BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER`, 45.`BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY`, 46.`BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT`, 47.`BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO`, 48.`BnSurfaceComposer::ADD_FPS_LISTENER`, 49.`BnSurfaceComposer::REMOVE_FPS_LISTENER`, 50.`BnSurfaceComposer::OVERRIDE_HDR_TYPES`, 51.`BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER`, 52.`BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER`, 53.`BnSurfaceComposer::ON_PULL_ATOM`, 54.`BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER`, 55.`BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER` | Value obtained from FuzzedDataProvider| +|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| +|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| +|`eventRegistration`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride` |Value obtained from FuzzedDataProvider| +|`frameRateCompatibility`| 0.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT`, 1.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE` |Value obtained from FuzzedDataProvider| +|`changeFrameRateStrategy`| 0.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS`, 1.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS` |Value obtained from FuzzedDataProvider| +|`hdrTypes`| 0.`ui::Hdr::DOLBY_VISION`, 1.`ui::Hdr::HDR10`, 2.`ui::Hdr::HLG`, 3.`ui::Hdr::HDR10_PLUS` |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_surfaceComposer_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_surfaceComposer_fuzzer/libgui_surfaceComposer_fuzzer +``` + +# <a name="libgui_surfaceComposerClient_fuzzer"></a> Fuzzer for SurfaceComposerClient + +SurfaceComposerClient supports the following data sources: +1. SurfaceWidth (parameter name:`width`) +2. SurfaceHeight (parameter name:`height`) +3. TransactionStateFlags (parameter name:`flags`) +4. TransformHint (parameter name:`outTransformHint`) +5. SurfacePixelFormat (parameter name:`format`) +6. LayerId (parameter name:`outLayerId`) +7. SurfaceComposerClientTags (parameter name:`surfaceTag`) +8. DefaultMode (parameter name:`defaultMode`) +9. PrimaryRefreshRateMin (parameter name:`primaryRefreshRateMin`) +10. PrimaryRefreshRateMax (parameter name:`primaryRefreshRateMax`) +11. AppRefreshRateMin (parameter name:`appRefreshRateMin`) +12. AppRefreshRateMax (parameter name:`appRefreshRateMax`) +13. DisplayPowerMode (parameter name:`mode`) +14. CacheId (parameter name:`cacheId`) +15. DisplayBrightness (parameter name:`brightness`) +16. PowerBoostID (parameter name:`boostId`) +17. AtomId (parameter name:`atomId`) +18. ComponentMask (parameter name:`componentMask`) +19. MaxFrames (parameter name:`maxFrames`) +20. TaskId (parameter name:`taskId`) +21. Alpha (parameter name:`aplha`) +22. CornerRadius (parameter name:`cornerRadius`) +23. BackgroundBlurRadius (parameter name:`backgroundBlurRadius`) +24. Half3Color (parameter name:`color`) +25. LayerStack (parameter name:`layerStack`) +26. Dataspace (parameter name:`dataspace`) +27. Api (parameter name:`api`) +28. Priority (parameter name:`priority`) +29. TouchableRegionPointX (parameter name:`pointX`) +30. TouchableRegionPointY (parameter name:`pointY`) +31. ColorMode (parameter name:`colorMode`) +32. WindowInfoFlags (parameter name:`flags`) +33. WindowInfoTransformOrientation (parameter name:`transform`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`surfaceTag`| 0.`Tag::CREATE_SURFACE`, 1.`Tag::CREATE_WITH_SURFACE_PARENT`, 2.`Tag::CLEAR_LAYER_FRAME_STATS`, 3.`Tag::GET_LAYER_FRAME_STATS`, 4.`Tag::MIRROR_SURFACE`, 5.`Tag::LAST` |Value obtained from FuzzedDataProvider| +|`mode`| 0.`gui::TouchOcclusionMode::BLOCK_UNTRUSTED`, 1.`gui::TouchOcclusionMode::USE_OPACITY`, 2.`gui::TouchOcclusionMode::ALLOW` |Value obtained from FuzzedDataProvider| +|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| +|`colorMode`|0.`ui::ColorMode::NATIVE`, 1.`ui::ColorMode::STANDARD_BT601_625`, 2.`ui::ColorMode::STANDARD_BT601_625_UNADJUSTED`, 3.`ui::ColorMode::STANDARD_BT601_525`, 4.`ui::ColorMode::STANDARD_BT601_525_UNADJUSTED`, 5.`ui::ColorMode::STANDARD_BT709`, 6.`ui::ColorMode::DCI_P3`, 7.`ui::ColorMode::SRGB`, 8.`ui::ColorMode::ADOBE_RGB`, 9.`ui::ColorMode::DISPLAY_P3`, 10.`ui::ColorMode::BT2020`, 11.`ui::ColorMode::BT2100_PQ`, 12.`ui::ColorMode::BT2100_HLG`, 13.`ui::ColorMode::DISPLAY_BT2020` |Value obtained from FuzzedDataProvider| +|`flags`|0 .`gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON`, 1.`gui::WindowInfo::Flag::DIM_BEHIND`, 2.`gui::WindowInfo::Flag::BLUR_BEHIND`, 3.`gui::WindowInfo::Flag::NOT_FOCUSABLE`, 4.`gui::WindowInfo::Flag::NOT_TOUCHABLE`, 5.`gui::WindowInfo::Flag::NOT_TOUCH_MODAL`, 6.`gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING`, 7.`gui::WindowInfo::Flag::KEEP_SCREEN_ON`, 8.`gui::WindowInfo::Flag::LAYOUT_IN_SCREEN`, 9.`gui::WindowInfo::Flag::LAYOUT_NO_LIMITS`, 10.`gui::WindowInfo::Flag::FULLSCREEN`, 11.`gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN`, 12.`gui::WindowInfo::Flag::DITHER`, 13.`gui::WindowInfo::Flag::SECURE`, 14.`gui::WindowInfo::Flag::SCALED`, 15.`gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES`, 16.`gui::WindowInfo::Flag::LAYOUT_INSET_DECOR`, 17.`gui::WindowInfo::Flag::ALT_FOCUSABLE_IM`, 18.`gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH`, 19.`gui::WindowInfo::Flag::SHOW_WHEN_LOCKED`, 20.`gui::WindowInfo::Flag::SHOW_WALLPAPER`, 21.`gui::WindowInfo::Flag::TURN_SCREEN_ON`, 22.`gui::WindowInfo::Flag::DISMISS_KEYGUARD`, 23.`gui::WindowInfo::Flag::SPLIT_TOUCH`, 24.`gui::WindowInfo::Flag::HARDWARE_ACCELERATED`, 25.`gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN`, 26.`gui::WindowInfo::Flag::TRANSLUCENT_STATUS`, 27.`gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION`, 28.`gui::WindowInfo::Flag::LOCAL_FOCUS_MODE`, 29.`gui::WindowInfo::Flag::SLIPPERY`, 30.`gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR`, 31.`gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS`, |Value obtained from FuzzedDataProvider| +|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| +|`transform`| 0.`ui::Transform::ROT_0`, 1.`ui::Transform::FLIP_H`, 2.`ui::Transform::FLIP_V`, 3.`ui::Transform::ROT_90`, 4.`ui::Transform::ROT_180`, 5.`ui::Transform::ROT_270` |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_surfaceComposerClient_fuzzer +``` +2. To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_surfaceComposerClient_fuzzer/libgui_surfaceComposerClient_fuzzer +``` + +# <a name="libgui_parcelable_fuzzer"></a> Fuzzer for Libgui_Parcelable + +Libgui_Parcelable supports the following parameters: +1. LayerMetadataKey (parameter name:`key`) +2. Dataspace (parameter name:`mDataspace`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`key`| 0.`view::LayerMetadataKey::METADATA_OWNER_UID`, 1.`view::LayerMetadataKey::METADATA_WINDOW_TYPE`, 2.`view::LayerMetadataKey::METADATA_TASK_ID`, 3.`view::LayerMetadataKey::METADATA_MOUSE_CURSOR`, 4.`view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID`, 5.`view::LayerMetadataKey::METADATA_OWNER_PID`, 6.`view::LayerMetadataKey::METADATA_DEQUEUE_TIME`, 7.`view::LayerMetadataKey::METADATA_GAME_MODE`, |Value obtained from FuzzedDataProvider| +|`mDataSpace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_fuzzer/libgui_fuzzer +``` + +# <a name="libgui_bufferQueue_fuzzer"></a> Fuzzer for BufferQueue + +BufferQueue supports the following parameters: +1. SurfaceWidth (parameter name:`width`) +2. SurfaceHeight (parameter name:`height`) +3. TransactionStateFlags (parameter name:`flags`) +4. TransformHint (parameter name:`outTransformHint`) +5. SurfacePixelFormat (parameter name:`format`) +6. LayerId (parameter name:`layerId`) +7. BufferId (parameter name:`bufferId`) +8. FrameNumber (parameter name:`frameNumber`) +9. FrameRate (parameter name:`frameRate`) +10. Compatability (parameter name:`compatability`) +11. LatchTime (parameter name:`latchTime`) +12. AcquireTime (parameter name:`acquireTime`) +13. RefreshTime (parameter name:`refreshTime`) +14. DequeueTime (parameter name:`dequeueTime`) +15. Slot (parameter name:`slot`) +16. MaxBuffers (parameter name:`maxBuffers`) +17. GenerationNumber (parameter name:`generationNumber`) +18. Api (parameter name:`api`) +19. Usage (parameter name:`usage`) +20. MaxFrameNumber (parameter name:`maxFrameNumber`) +21. BufferCount (parameter name:`bufferCount`) +22. MaxAcquredBufferCount (parameter name:`maxAcquredBufferCount`) +23. Status (parameter name:`status`) +24. ApiConnection (parameter name:`apiConnection`) +25. Dataspace (parameter name:`dataspace`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`status`| 0.`OK`, 1.`NO_MEMORY`, 2.`NO_INIT`, 3.`BAD_VALUE`, 4.`DEAD_OBJECT`, 5.`INVALID_OPERATION`, 6.`TIMED_OUT`, 7.`WOULD_BLOCK`, 8.`UNKNOWN_ERROR`, 9.`ALREADY_EXISTS`, |Value obtained from FuzzedDataProvider| +|`apiConnection`| 0.`BufferQueueCore::CURRENTLY_CONNECTED_API`, 1.`BufferQueueCore::NO_CONNECTED_API`, 2.`NATIVE_WINDOW_API_EGL`, 3.`NATIVE_WINDOW_API_CPU`, 4.`NATIVE_WINDOW_API_MEDIA`, 5.`NATIVE_WINDOW_API_CAMERA`, |Value obtained from FuzzedDataProvider| +|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_bufferQueue_fuzzer +``` +2. To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_bufferQueue_fuzzer/libgui_bufferQueue_fuzzer +``` + +# <a name="libgui_consumer_fuzzer"></a> Fuzzer for Libgui_Consumer + +Libgui_Consumer supports the following parameters: +1. GraphicWidth (parameter name:`graphicWidth`) +2. GraphicHeight (parameter name:`graphicHeight`) +4. TransformHint (parameter name:`outTransformHint`) +5. GraphicPixelFormat (parameter name:`format`) +6. Usage (parameter name:`usage`) + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_consumer_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_consumer_fuzzer/libgui_consumer_fuzzer +``` + +# <a name="libgui_displayEvent_fuzzer"></a> Fuzzer for LibGui_DisplayEvent + +LibGui_DisplayEvent supports the following parameters: +1. DisplayEventType (parameter name:`type`) +2. Events (parameter name:`events`) +3. VsyncSource (parameter name:`vsyncSource`) +4. EventRegistrationFlags (parameter name:`flags`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| +|`flags`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride`, |Value obtained from FuzzedDataProvider| +|`type`| 0.`DisplayEventReceiver::DISPLAY_EVENT_NULL`, 1.`DisplayEventReceiver::DISPLAY_EVENT_VSYNC`, 2.`DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG`, 3.`DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE`, 4.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE`, 5.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH`, |Value obtained from FuzzedDataProvider| +|`events`| 0.`Looper::EVENT_INPUT`, 1.`Looper::EVENT_OUTPUT`, 2.`Looper::EVENT_ERROR`, 3.`Looper::EVENT_HANGUP`, 4.`Looper::EVENT_INVALID`, |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) libgui_displayEvent_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libgui_displayEvent_fuzzer/libgui_displayEvent_fuzzer +``` diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp new file mode 100644 index 0000000000..761f08feb6 --- /dev/null +++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp @@ -0,0 +1,392 @@ +/* + * Copyright 2022 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 <android-base/stringprintf.h> +#include <gui/BufferQueueConsumer.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <system/window.h> + +#include <libgui_fuzzer_utils.h> + +using namespace android; +using namespace hardware::graphics::bufferqueue; +using namespace V1_0::utils; +using namespace V2_0::utils; + +constexpr int32_t kMaxBytes = 256; + +constexpr int32_t kError[] = { + OK, NO_MEMORY, NO_INIT, BAD_VALUE, DEAD_OBJECT, INVALID_OPERATION, + TIMED_OUT, WOULD_BLOCK, UNKNOWN_ERROR, ALREADY_EXISTS, +}; + +constexpr int32_t kAPIConnection[] = { + BufferQueueCore::CURRENTLY_CONNECTED_API, + BufferQueueCore::NO_CONNECTED_API, + NATIVE_WINDOW_API_EGL, + NATIVE_WINDOW_API_CPU, + NATIVE_WINDOW_API_MEDIA, + NATIVE_WINDOW_API_CAMERA, +}; + +class BufferQueueFuzzer { +public: + BufferQueueFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + void invokeTypes(); + void invokeH2BGraphicBufferV1(); + void invokeH2BGraphicBufferV2(); + void invokeBufferQueueConsumer(); + void invokeBufferQueueProducer(); + void invokeBlastBufferQueue(); + void invokeQuery(sp<BufferQueueProducer>); + void invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer>); + void invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer>); + void invokeAcquireBuffer(sp<BufferQueueConsumer>); + void invokeOccupancyTracker(sp<BufferQueueConsumer>); + sp<SurfaceControl> makeSurfaceControl(); + sp<BLASTBufferQueue> makeBLASTBufferQueue(sp<SurfaceControl>); + + FuzzedDataProvider mFdp; +}; + +class ManageResourceHandle { +public: + ManageResourceHandle(FuzzedDataProvider* fdp) { + mNativeHandle = native_handle_create(0 /*numFds*/, 1 /*numInts*/); + mShouldOwn = fdp->ConsumeBool(); + mStream = NativeHandle::create(mNativeHandle, mShouldOwn); + } + ~ManageResourceHandle() { + if (!mShouldOwn) { + native_handle_close(mNativeHandle); + native_handle_delete(mNativeHandle); + } + } + sp<NativeHandle> getStream() { return mStream; } + +private: + bool mShouldOwn; + sp<NativeHandle> mStream; + native_handle_t* mNativeHandle; +}; + +sp<SurfaceControl> BufferQueueFuzzer::makeSurfaceControl() { + sp<IBinder> handle; + const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); + sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); + sp<BnGraphicBufferProducer> producer; + uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>(); + std::string layerName = base::StringPrintf("#%d", layerId); + return sp<SurfaceControl>::make(client, handle, layerId, layerName, + mFdp.ConsumeIntegral<int32_t>(), + mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<int32_t>(), + mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()); +} + +sp<BLASTBufferQueue> BufferQueueFuzzer::makeBLASTBufferQueue(sp<SurfaceControl> surface) { + return sp<BLASTBufferQueue>::make(mFdp.ConsumeRandomLengthString(kMaxBytes), surface, + mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<int32_t>()); +} + +void BufferQueueFuzzer::invokeBlastBufferQueue() { + sp<SurfaceControl> surface = makeSurfaceControl(); + sp<BLASTBufferQueue> queue = makeBLASTBufferQueue(surface); + + BufferItem item; + queue->onFrameAvailable(item); + queue->onFrameReplaced(item); + uint64_t bufferId = mFdp.ConsumeIntegral<uint64_t>(); + queue->onFrameDequeued(bufferId); + queue->onFrameCancelled(bufferId); + + SurfaceComposerClient::Transaction next; + uint64_t frameNumber = mFdp.ConsumeIntegral<uint64_t>(); + queue->mergeWithNextTransaction(&next, frameNumber); + queue->applyPendingTransactions(frameNumber); + + queue->update(surface, mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<int32_t>()); + queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(), + mFdp.ConsumeBool() /*shouldBeSeamless*/); + FrameTimelineInfo info; + queue->setFrameTimelineInfo(info); + + ManageResourceHandle handle(&mFdp); + queue->setSidebandStream(handle.getStream()); + + queue->getLastTransformHint(); + queue->getLastAcquiredFrameNum(); + + CompositorTiming compTiming; + sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING)); + sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING)); + FrameEventHistoryStats frameStats(frameNumber, gpuFence, compTiming, + mFdp.ConsumeIntegral<int64_t>(), + mFdp.ConsumeIntegral<int64_t>()); + std::vector<SurfaceControlStats> stats; + sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); + SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(), + mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence, + mFdp.ConsumeIntegral<uint32_t>(), frameStats, + mFdp.ConsumeIntegral<uint32_t>()); + stats.push_back(controlStats); +} + +void BufferQueueFuzzer::invokeQuery(sp<BufferQueueProducer> producer) { + int32_t value; + producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); +} + +void BufferQueueFuzzer::invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer> producer) { + int32_t value; + producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); +} + +void BufferQueueFuzzer::invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer> producer) { + int32_t value; + producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); +} + +void BufferQueueFuzzer::invokeBufferQueueProducer() { + sp<BufferQueueCore> core(new BufferQueueCore()); + sp<BufferQueueProducer> producer(new BufferQueueProducer(core)); + const sp<android::IProducerListener> listener; + android::IGraphicBufferProducer::QueueBufferOutput output; + uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); + producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); + + sp<GraphicBuffer> buffer; + int32_t slot = mFdp.ConsumeIntegral<int32_t>(); + uint32_t maxBuffers = mFdp.ConsumeIntegral<uint32_t>(); + producer->requestBuffer(slot, &buffer); + producer->setMaxDequeuedBufferCount(maxBuffers); + producer->setAsyncMode(mFdp.ConsumeBool() /*async*/); + + android::IGraphicBufferProducer::QueueBufferInput input; + producer->attachBuffer(&slot, buffer); + producer->queueBuffer(slot, input, &output); + + int32_t format = mFdp.ConsumeIntegral<int32_t>(); + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); + uint64_t outBufferAge; + FrameEventHistoryDelta outTimestamps; + sp<android::Fence> fence; + producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, + &outTimestamps); + producer->detachBuffer(slot); + producer->detachNextBuffer(&buffer, &fence); + producer->cancelBuffer(slot, fence); + + invokeQuery(producer); + + ManageResourceHandle handle(&mFdp); + producer->setSidebandStream(handle.getStream()); + + producer->allocateBuffers(width, height, format, usage); + producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); + producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); + producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); + producer->setLegacyBufferDrop(mFdp.ConsumeBool() /*drop*/); + producer->setAutoPrerotation(mFdp.ConsumeBool() /*autoPrerotation*/); + + producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); + producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); + producer->disconnect(api); +} + +void BufferQueueFuzzer::invokeAcquireBuffer(sp<BufferQueueConsumer> consumer) { + BufferItem item; + consumer->acquireBuffer(&item, mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint64_t>()); +} + +void BufferQueueFuzzer::invokeOccupancyTracker(sp<BufferQueueConsumer> consumer) { + String8 outResult; + String8 prefix((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); + consumer->dumpState(prefix, &outResult); + + std::vector<OccupancyTracker::Segment> outHistory; + consumer->getOccupancyHistory(mFdp.ConsumeBool() /*forceFlush*/, &outHistory); +} + +void BufferQueueFuzzer::invokeBufferQueueConsumer() { + sp<BufferQueueCore> core(new BufferQueueCore()); + sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core)); + sp<android::IConsumerListener> listener; + consumer->consumerConnect(listener, mFdp.ConsumeBool() /*controlledByApp*/); + invokeAcquireBuffer(consumer); + + int32_t slot = mFdp.ConsumeIntegral<int32_t>(); + sp<GraphicBuffer> buffer = + new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint64_t>()); + consumer->attachBuffer(&slot, buffer); + consumer->detachBuffer(slot); + + consumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()); + consumer->setMaxBufferCount(mFdp.ConsumeIntegral<int32_t>()); + consumer->setMaxAcquiredBufferCount(mFdp.ConsumeIntegral<int32_t>()); + + String8 name((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); + consumer->setConsumerName(name); + consumer->setDefaultBufferFormat(mFdp.ConsumeIntegral<int32_t>()); + android_dataspace dataspace = + static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); + consumer->setDefaultBufferDataSpace(dataspace); + + consumer->setTransformHint(mFdp.ConsumeIntegral<uint32_t>()); + consumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); + consumer->setConsumerIsProtected(mFdp.ConsumeBool() /*isProtected*/); + invokeOccupancyTracker(consumer); + + sp<Fence> releaseFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); + consumer->releaseBuffer(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint64_t>(), + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); + consumer->consumerDisconnect(); +} + +void BufferQueueFuzzer::invokeTypes() { + HStatus hStatus; + int32_t status = mFdp.PickValueInArray(kError); + bool bufferNeedsReallocation = mFdp.ConsumeBool(); + bool releaseAllBuffers = mFdp.ConsumeBool(); + b2h(status, &hStatus, &bufferNeedsReallocation, &releaseAllBuffers); + h2b(hStatus, &status); + + HConnectionType type; + int32_t apiConnection = mFdp.PickValueInArray(kAPIConnection); + b2h(apiConnection, &type); + h2b(type, &apiConnection); +} + +void BufferQueueFuzzer::invokeH2BGraphicBufferV1() { + sp<V1_0::utils::H2BGraphicBufferProducer> producer( + new V1_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV1())); + const sp<android::IProducerListener> listener; + android::IGraphicBufferProducer::QueueBufferOutput output; + uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); + producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); + + sp<GraphicBuffer> buffer; + int32_t slot = mFdp.ConsumeIntegral<int32_t>(); + producer->requestBuffer(slot, &buffer); + producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<int32_t>()); + producer->setAsyncMode(mFdp.ConsumeBool()); + + android::IGraphicBufferProducer::QueueBufferInput input; + input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); + producer->attachBuffer(&slot, buffer); + producer->queueBuffer(slot, input, &output); + + int32_t format = mFdp.ConsumeIntegral<int32_t>(); + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); + uint64_t outBufferAge; + FrameEventHistoryDelta outTimestamps; + sp<android::Fence> fence; + producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, + &outTimestamps); + producer->detachBuffer(slot); + producer->cancelBuffer(slot, fence); + + invokeQuery(producer); + + ManageResourceHandle handle(&mFdp); + producer->setSidebandStream(handle.getStream()); + + producer->allocateBuffers(width, height, format, usage); + producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); + producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); + producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); + + producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); + producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); + producer->disconnect(api); +} + +void BufferQueueFuzzer::invokeH2BGraphicBufferV2() { + sp<V2_0::utils::H2BGraphicBufferProducer> producer( + new V2_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV2())); + const sp<android::IProducerListener> listener; + android::IGraphicBufferProducer::QueueBufferOutput output; + uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); + producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); + + sp<GraphicBuffer> buffer; + int32_t slot = mFdp.ConsumeIntegral<int32_t>(); + producer->requestBuffer(slot, &buffer); + producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<uint32_t>()); + producer->setAsyncMode(mFdp.ConsumeBool()); + + android::IGraphicBufferProducer::QueueBufferInput input; + input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); + producer->attachBuffer(&slot, buffer); + producer->queueBuffer(slot, input, &output); + + int32_t format = mFdp.ConsumeIntegral<int32_t>(); + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); + uint64_t outBufferAge; + FrameEventHistoryDelta outTimestamps; + sp<android::Fence> fence; + producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, + &outTimestamps); + producer->detachBuffer(slot); + producer->cancelBuffer(slot, fence); + + invokeQuery(producer); + + ManageResourceHandle handle(&mFdp); + producer->setSidebandStream(handle.getStream()); + + producer->allocateBuffers(width, height, format, usage); + producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); + producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); + producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); + + producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); + producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); + producer->disconnect(api); +} + +void BufferQueueFuzzer::process() { + invokeBlastBufferQueue(); + invokeH2BGraphicBufferV1(); + invokeH2BGraphicBufferV2(); + invokeTypes(); + invokeBufferQueueConsumer(); + invokeBufferQueueProducer(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + BufferQueueFuzzer bufferQueueFuzzer(data, size); + bufferQueueFuzzer.process(); + return 0; +} diff --git a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp b/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp new file mode 100644 index 0000000000..24a046d3a9 --- /dev/null +++ b/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2022 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/BufferQueueConsumer.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> +#include <gui/GLConsumer.h> +#include <libgui_fuzzer_utils.h> + +using namespace android; + +constexpr int32_t kMinBuffer = 0; +constexpr int32_t kMaxBuffer = 100000; + +class ConsumerFuzzer { +public: + ConsumerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + FuzzedDataProvider mFdp; +}; + +void ConsumerFuzzer::process() { + sp<BufferQueueCore> core(new BufferQueueCore()); + sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); + + uint64_t maxBuffers = mFdp.ConsumeIntegralInRange<uint64_t>(kMinBuffer, kMaxBuffer); + sp<CpuConsumer> cpu( + new CpuConsumer(consumer, maxBuffers, mFdp.ConsumeBool() /*controlledByApp*/)); + CpuConsumer::LockedBuffer lockBuffer; + cpu->lockNextBuffer(&lockBuffer); + cpu->unlockBuffer(lockBuffer); + cpu->abandon(); + + uint32_t tex = mFdp.ConsumeIntegral<uint32_t>(); + sp<GLConsumer> glComsumer(new GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, + mFdp.ConsumeBool() /*useFenceSync*/, + mFdp.ConsumeBool() /*isControlledByApp*/)); + sp<Fence> releaseFence = new Fence(memfd_create("rfd", MFD_ALLOW_SEALING)); + glComsumer->setReleaseFence(releaseFence); + glComsumer->updateTexImage(); + glComsumer->releaseTexImage(); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint64_t>()); + float mtx[16]; + glComsumer->getTransformMatrix(mtx); + glComsumer->computeTransformMatrix(mtx, buffer, getRect(&mFdp), + mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeBool() /*filtering*/); + glComsumer->scaleDownCrop(getRect(&mFdp), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()); + + glComsumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()); + glComsumer->setFilteringEnabled(mFdp.ConsumeBool() /*enabled*/); + + glComsumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); + glComsumer->attachToContext(tex); + glComsumer->abandon(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + ConsumerFuzzer consumerFuzzer(data, size); + consumerFuzzer.process(); + return 0; +} diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp new file mode 100644 index 0000000000..6d5ae49635 --- /dev/null +++ b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2022 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 <android/gui/ISurfaceComposer.h> + +#include <libgui_fuzzer_utils.h> + +using namespace android; + +constexpr gui::ISurfaceComposer::VsyncSource kVsyncSource[] = { + gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, + gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, +}; + +constexpr gui::ISurfaceComposer::EventRegistration kEventRegistration[] = { + gui::ISurfaceComposer::EventRegistration::modeChanged, + gui::ISurfaceComposer::EventRegistration::frameRateOverride, +}; + +constexpr uint32_t kDisplayEvent[] = { + DisplayEventReceiver::DISPLAY_EVENT_NULL, + DisplayEventReceiver::DISPLAY_EVENT_VSYNC, + DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, + DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, + DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, + DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, +}; + +constexpr int32_t kEvents[] = { + Looper::EVENT_INPUT, Looper::EVENT_OUTPUT, Looper::EVENT_ERROR, + Looper::EVENT_HANGUP, Looper::EVENT_INVALID, +}; + +DisplayEventReceiver::Event buildDisplayEvent(FuzzedDataProvider* fdp, uint32_t type, + DisplayEventReceiver::Event event) { + switch (type) { + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: { + event.vsync.count = fdp->ConsumeIntegral<uint32_t>(); + event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>(); + event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>(); + for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesLength; ++idx) { + event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>(); + event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp = + fdp->ConsumeIntegral<uint64_t>(); + event.vsync.vsyncData.frameTimelines[idx].expectedPresentationTime = + fdp->ConsumeIntegral<uint64_t>(); + } + break; + + } + case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: { + event.hotplug = DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/}; + break; + } + case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { + event.modeChange = + DisplayEventReceiver::Event::ModeChange{fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int64_t>()}; + break; + } + case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: + case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: { + event.frameRateOverride = + DisplayEventReceiver::Event::FrameRateOverride{fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeFloatingPoint< + float>()}; + break; + } + } + return event; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + sp<Looper> looper; + sp<FakeDisplayEventDispatcher> dispatcher( + new FakeDisplayEventDispatcher(looper, fdp.PickValueInArray(kVsyncSource), + fdp.PickValueInArray(kEventRegistration))); + + dispatcher->initialize(); + DisplayEventReceiver::Event event; + uint32_t type = fdp.PickValueInArray(kDisplayEvent); + PhysicalDisplayId displayId; + event.header = + DisplayEventReceiver::Event::Header{type, displayId, fdp.ConsumeIntegral<int64_t>()}; + event = buildDisplayEvent(&fdp, type, event); + + dispatcher->injectEvent(event); + dispatcher->handleEvent(0, fdp.PickValueInArray(kEvents), nullptr); + return 0; +} diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h new file mode 100644 index 0000000000..8810e4e83a --- /dev/null +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -0,0 +1,310 @@ +/* + * Copyright 2021 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 <android/gui/BnRegionSamplingListener.h> +#include <android/gui/BnSurfaceComposer.h> +#include <android/gui/BnSurfaceComposerClient.h> +#include <android/gui/IDisplayEventConnection.h> +#include <android/gui/ISurfaceComposerClient.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <gmock/gmock.h> +#include <gui/BLASTBufferQueue.h> +#include <gui/DisplayEventDispatcher.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/LayerDebugInfo.h> +#include <gui/LayerState.h> +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> +#include <ui/fuzzer/FuzzableDataspaces.h> + +namespace android { + +constexpr uint32_t kOrientation[] = { + ui::Transform::ROT_0, ui::Transform::FLIP_H, ui::Transform::FLIP_V, + ui::Transform::ROT_90, ui::Transform::ROT_180, ui::Transform::ROT_270, +}; + +Rect getRect(FuzzedDataProvider* fdp) { + const int32_t left = fdp->ConsumeIntegral<int32_t>(); + const int32_t top = fdp->ConsumeIntegral<int32_t>(); + const int32_t right = fdp->ConsumeIntegral<int32_t>(); + const int32_t bottom = fdp->ConsumeIntegral<int32_t>(); + return Rect(left, top, right, bottom); +} + +gui::DisplayBrightness getBrightness(FuzzedDataProvider* fdp) { + static constexpr float kMinBrightness = 0; + static constexpr float kMaxBrightness = 1; + gui::DisplayBrightness brightness; + brightness.sdrWhitePoint = + fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); + brightness.sdrWhitePointNits = + fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); + brightness.displayBrightness = + fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); + brightness.displayBrightnessNits = + fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); + return brightness; +} + +class FakeBnSurfaceComposer : public gui::BnSurfaceComposer { +public: + MOCK_METHOD(binder::Status, bootFinished, (), (override)); + MOCK_METHOD(binder::Status, createDisplayEventConnection, + (gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration, + sp<gui::IDisplayEventConnection>*), + (override)); + MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override)); + MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, sp<IBinder>*), + (override)); + MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override)); + MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override)); + MOCK_METHOD(binder::Status, getPhysicalDisplayToken, (int64_t, sp<IBinder>*), (override)); + MOCK_METHOD(binder::Status, setPowerMode, (const sp<IBinder>&, int), (override)); + MOCK_METHOD(binder::Status, getSupportedFrameTimestamps, (std::vector<FrameEvent>*), + (override)); + MOCK_METHOD(binder::Status, getDisplayStats, (const sp<IBinder>&, gui::DisplayStatInfo*), + (override)); + MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*), + (override)); + MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*), + (override)); + MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*), + (override)); + MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken, + (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override)); + MOCK_METHOD(binder::Status, getDisplayNativePrimaries, + (const sp<IBinder>&, gui::DisplayPrimaries*), (override)); + MOCK_METHOD(binder::Status, setActiveColorMode, (const sp<IBinder>&, int), (override)); + MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override)); + MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override)); + MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override)); + MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override)); + MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override)); + MOCK_METHOD(binder::Status, captureDisplay, + (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); + MOCK_METHOD(binder::Status, captureDisplayById, (int64_t, const sp<IScreenCaptureListener>&), + (override)); + MOCK_METHOD(binder::Status, captureLayers, + (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); + MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override)); + MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override)); + MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&), + (override)); + MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override)); + MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override)); + MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override)); + MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*), + (override)); + MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes, + (const sp<IBinder>&, gui::ContentSamplingAttributes*), (override)); + MOCK_METHOD(binder::Status, setDisplayContentSamplingEnabled, + (const sp<IBinder>&, bool, int8_t, int64_t), (override)); + MOCK_METHOD(binder::Status, getDisplayedContentSample, + (const sp<IBinder>&, int64_t, int64_t, gui::DisplayedFrameStats*), (override)); + MOCK_METHOD(binder::Status, getProtectedContentSupport, (bool*), (override)); + MOCK_METHOD(binder::Status, isWideColorDisplay, (const sp<IBinder>&, bool*), (override)); + MOCK_METHOD(binder::Status, addRegionSamplingListener, + (const gui::ARect&, const sp<IBinder>&, const sp<gui::IRegionSamplingListener>&), + (override)); + MOCK_METHOD(binder::Status, removeRegionSamplingListener, + (const sp<gui::IRegionSamplingListener>&), (override)); + MOCK_METHOD(binder::Status, addFpsListener, (int32_t, const sp<gui::IFpsListener>&), + (override)); + MOCK_METHOD(binder::Status, removeFpsListener, (const sp<gui::IFpsListener>&), (override)); + MOCK_METHOD(binder::Status, addTunnelModeEnabledListener, + (const sp<gui::ITunnelModeEnabledListener>&), (override)); + MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener, + (const sp<gui::ITunnelModeEnabledListener>&), (override)); + MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs, + (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override)); + MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs, + (const sp<IBinder>&, gui::DisplayModeSpecs*), (override)); + MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*), + (override)); + MOCK_METHOD(binder::Status, setDisplayBrightness, + (const sp<IBinder>&, const gui::DisplayBrightness&), (override)); + MOCK_METHOD(binder::Status, addHdrLayerInfoListener, + (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); + MOCK_METHOD(binder::Status, removeHdrLayerInfoListener, + (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); + MOCK_METHOD(binder::Status, notifyPowerBoost, (int), (override)); + MOCK_METHOD(binder::Status, setGlobalShadowSettings, + (const gui::Color&, const gui::Color&, float, float, float), (override)); + MOCK_METHOD(binder::Status, getDisplayDecorationSupport, + (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override)); + MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override)); + MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override)); + MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override)); + MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&), + (override)); + MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&), + (override)); + MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override)); +}; + +class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient { +public: + MOCK_METHOD(binder::Status, createSurface, + (const std::string& name, int32_t flags, const sp<IBinder>& parent, + const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult), + (override)); + + MOCK_METHOD(binder::Status, clearLayerFrameStats, (const sp<IBinder>& handle), (override)); + + MOCK_METHOD(binder::Status, getLayerFrameStats, + (const sp<IBinder>& handle, gui::FrameStats* outStats), (override)); + + MOCK_METHOD(binder::Status, mirrorSurface, + (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult), + (override)); + + MOCK_METHOD(binder::Status, mirrorDisplay, + (int64_t displayId, gui::CreateSurfaceResult* outResult), (override)); +}; + +class FakeDisplayEventDispatcher : public DisplayEventDispatcher { +public: + FakeDisplayEventDispatcher(const sp<Looper>& looper, + gui::ISurfaceComposer::VsyncSource vsyncSource, + gui::ISurfaceComposer::EventRegistration eventRegistration) + : DisplayEventDispatcher(looper, vsyncSource, eventRegistration){}; + + MOCK_METHOD4(dispatchVsync, void(nsecs_t, PhysicalDisplayId, uint32_t, VsyncEventData)); + MOCK_METHOD3(dispatchHotplug, void(nsecs_t, PhysicalDisplayId, bool)); + MOCK_METHOD4(dispatchModeChanged, void(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t)); + MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId)); + MOCK_METHOD3(dispatchFrameRateOverrides, + void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>)); +}; + +} // namespace android + +namespace android::hardware { + +namespace graphics::bufferqueue::V1_0::utils { + +class FakeGraphicBufferProducerV1 : public HGraphicBufferProducer { +public: + FakeGraphicBufferProducerV1() { + ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return 0; }); + ON_CALL(*this, setAsyncMode).WillByDefault([]() { return 0; }); + ON_CALL(*this, detachBuffer).WillByDefault([]() { return 0; }); + ON_CALL(*this, cancelBuffer).WillByDefault([]() { return 0; }); + ON_CALL(*this, disconnect).WillByDefault([]() { return 0; }); + ON_CALL(*this, setSidebandStream).WillByDefault([]() { return 0; }); + ON_CALL(*this, allowAllocation).WillByDefault([]() { return 0; }); + ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return 0; }); + ON_CALL(*this, setSharedBufferMode).WillByDefault([]() { return 0; }); + ON_CALL(*this, setAutoRefresh).WillByDefault([]() { return 0; }); + ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return 0; }); + ON_CALL(*this, setLegacyBufferDrop).WillByDefault([]() { return 0; }); + }; + MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); + MOCK_METHOD1(setMaxDequeuedBufferCount, Return<int32_t>(int32_t)); + MOCK_METHOD1(setAsyncMode, Return<int32_t>(bool)); + MOCK_METHOD6(dequeueBuffer, + Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t, + bool, dequeueBuffer_cb)); + MOCK_METHOD1(detachBuffer, Return<int32_t>(int)); + MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); + MOCK_METHOD2(attachBuffer, Return<void>(const media::V1_0::AnwBuffer&, attachBuffer_cb)); + MOCK_METHOD3( + queueBuffer, + Return<void>( + int, + const graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput&, + queueBuffer_cb)); + MOCK_METHOD2(cancelBuffer, Return<int32_t>(int, const hidl_handle&)); + MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); + MOCK_METHOD4(connect, + Return<void>(const sp<graphics::bufferqueue::V1_0::IProducerListener>&, int32_t, + bool, connect_cb)); + MOCK_METHOD2(disconnect, + Return<int32_t>( + int, graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode)); + MOCK_METHOD1(setSidebandStream, Return<int32_t>(const hidl_handle&)); + MOCK_METHOD4(allocateBuffers, + Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t)); + MOCK_METHOD1(allowAllocation, Return<int32_t>(bool)); + MOCK_METHOD1(setGenerationNumber, Return<int32_t>(uint32_t)); + MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); + MOCK_METHOD1(setSharedBufferMode, Return<int32_t>(bool)); + MOCK_METHOD1(setAutoRefresh, Return<int32_t>(bool)); + MOCK_METHOD1(setDequeueTimeout, Return<int32_t>(nsecs_t)); + MOCK_METHOD1(setLegacyBufferDrop, Return<int32_t>(bool)); + MOCK_METHOD1(getLastQueuedBuffer, Return<void>(getLastQueuedBuffer_cb)); + MOCK_METHOD1(getFrameTimestamps, Return<void>(getFrameTimestamps_cb)); + MOCK_METHOD1(getUniqueId, Return<void>(getUniqueId_cb)); +}; + +}; // namespace graphics::bufferqueue::V1_0::utils + +namespace graphics::bufferqueue::V2_0::utils { + +class FakeGraphicBufferProducerV2 : public HGraphicBufferProducer { +public: + FakeGraphicBufferProducerV2() { + ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, setAsyncMode).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, detachBuffer).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, cancelBuffer).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, disconnect).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, allocateBuffers).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, allowAllocation).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return Status::OK; }); + ON_CALL(*this, getUniqueId).WillByDefault([]() { return 0; }); + }; + MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); + MOCK_METHOD1(setMaxDequeuedBufferCount, Return<graphics::bufferqueue::V2_0::Status>(int)); + MOCK_METHOD1(setAsyncMode, Return<graphics::bufferqueue::V2_0::Status>(bool)); + MOCK_METHOD2( + dequeueBuffer, + Return<void>( + const graphics::bufferqueue::V2_0::IGraphicBufferProducer::DequeueBufferInput&, + dequeueBuffer_cb)); + MOCK_METHOD1(detachBuffer, Return<graphics::bufferqueue::V2_0::Status>(int)); + MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); + MOCK_METHOD3(attachBuffer, + Return<void>(const graphics::common::V1_2::HardwareBuffer&, uint32_t, + attachBuffer_cb)); + MOCK_METHOD3( + queueBuffer, + Return<void>( + int, + const graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferInput&, + queueBuffer_cb)); + MOCK_METHOD2(cancelBuffer, + Return<graphics::bufferqueue::V2_0::Status>(int, const hidl_handle&)); + MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); + MOCK_METHOD4(connect, + Return<void>(const sp<graphics::bufferqueue::V2_0::IProducerListener>&, + graphics::bufferqueue::V2_0::ConnectionType, bool, connect_cb)); + MOCK_METHOD1(disconnect, + Return<graphics::bufferqueue::V2_0::Status>( + graphics::bufferqueue::V2_0::ConnectionType)); + MOCK_METHOD4(allocateBuffers, + Return<graphics::bufferqueue::V2_0::Status>(uint32_t, uint32_t, uint32_t, + uint64_t)); + MOCK_METHOD1(allowAllocation, Return<graphics::bufferqueue::V2_0::Status>(bool)); + MOCK_METHOD1(setGenerationNumber, Return<graphics::bufferqueue::V2_0::Status>(uint32_t)); + MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); + MOCK_METHOD1(setDequeueTimeout, Return<graphics::bufferqueue::V2_0::Status>(int64_t)); + MOCK_METHOD0(getUniqueId, Return<uint64_t>()); +}; + +}; // namespace graphics::bufferqueue::V2_0::utils +}; // namespace android::hardware diff --git a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp b/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp new file mode 100644 index 0000000000..9f0f6cac19 --- /dev/null +++ b/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2022 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/BufferQueueConsumer.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> +#include <gui/LayerMetadata.h> +#include <gui/OccupancyTracker.h> +#include <gui/StreamSplitter.h> +#include <gui/Surface.h> +#include <gui/SurfaceControl.h> +#include <gui/view/Surface.h> +#include <libgui_fuzzer_utils.h> +#include "android/view/LayerMetadataKey.h" + +using namespace android; + +constexpr int32_t kMaxBytes = 256; +constexpr int32_t kMatrixSize = 4; +constexpr int32_t kLayerMetadataKeyCount = 8; + +constexpr uint32_t kMetadataKey[] = { + (uint32_t)view::LayerMetadataKey::METADATA_OWNER_UID, + (uint32_t)view::LayerMetadataKey::METADATA_WINDOW_TYPE, + (uint32_t)view::LayerMetadataKey::METADATA_TASK_ID, + (uint32_t)view::LayerMetadataKey::METADATA_MOUSE_CURSOR, + (uint32_t)view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID, + (uint32_t)view::LayerMetadataKey::METADATA_OWNER_PID, + (uint32_t)view::LayerMetadataKey::METADATA_DEQUEUE_TIME, + (uint32_t)view::LayerMetadataKey::METADATA_GAME_MODE, +}; + +class ParcelableFuzzer { +public: + ParcelableFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + void invokeStreamSplitter(); + void invokeOccupancyTracker(); + void invokeLayerDebugInfo(); + void invokeLayerMetadata(); + void invokeViewSurface(); + + FuzzedDataProvider mFdp; +}; + +void ParcelableFuzzer::invokeViewSurface() { + view::Surface surface; + surface.name = String16((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); + Parcel parcel; + surface.writeToParcel(&parcel); + parcel.setDataPosition(0); + surface.readFromParcel(&parcel); + bool nameAlreadyWritten = mFdp.ConsumeBool(); + surface.writeToParcel(&parcel, nameAlreadyWritten); + parcel.setDataPosition(0); + surface.readFromParcel(&parcel, mFdp.ConsumeBool()); +} + +void ParcelableFuzzer::invokeLayerMetadata() { + std::unordered_map<uint32_t, std::vector<uint8_t>> map; + for (size_t idx = 0; idx < kLayerMetadataKeyCount; ++idx) { + std::vector<uint8_t> data; + for (size_t idx1 = 0; idx1 < mFdp.ConsumeIntegral<uint32_t>(); ++idx1) { + data.push_back(mFdp.ConsumeIntegral<uint8_t>()); + } + map[kMetadataKey[idx]] = data; + } + LayerMetadata metadata(map); + uint32_t key = mFdp.PickValueInArray(kMetadataKey); + metadata.setInt32(key, mFdp.ConsumeIntegral<int32_t>()); + metadata.itemToString(key, (mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); + + Parcel parcel; + metadata.writeToParcel(&parcel); + parcel.setDataPosition(0); + metadata.readFromParcel(&parcel); +} + +void ParcelableFuzzer::invokeLayerDebugInfo() { + gui::LayerDebugInfo info; + info.mName = mFdp.ConsumeRandomLengthString(kMaxBytes); + info.mParentName = mFdp.ConsumeRandomLengthString(kMaxBytes); + info.mType = mFdp.ConsumeRandomLengthString(kMaxBytes); + info.mLayerStack = mFdp.ConsumeIntegral<uint32_t>(); + info.mX = mFdp.ConsumeFloatingPoint<float>(); + info.mY = mFdp.ConsumeFloatingPoint<float>(); + info.mZ = mFdp.ConsumeIntegral<uint32_t>(); + info.mWidth = mFdp.ConsumeIntegral<int32_t>(); + info.mHeight = mFdp.ConsumeIntegral<int32_t>(); + info.mActiveBufferWidth = mFdp.ConsumeIntegral<int32_t>(); + info.mActiveBufferHeight = mFdp.ConsumeIntegral<int32_t>(); + info.mActiveBufferStride = mFdp.ConsumeIntegral<int32_t>(); + info.mActiveBufferFormat = mFdp.ConsumeIntegral<int32_t>(); + info.mNumQueuedFrames = mFdp.ConsumeIntegral<int32_t>(); + + info.mFlags = mFdp.ConsumeIntegral<uint32_t>(); + info.mPixelFormat = mFdp.ConsumeIntegral<int32_t>(); + info.mTransparentRegion = Region(getRect(&mFdp)); + info.mVisibleRegion = Region(getRect(&mFdp)); + info.mSurfaceDamageRegion = Region(getRect(&mFdp)); + info.mCrop = getRect(&mFdp); + info.mDataSpace = static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); + info.mColor = half4(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); + for (size_t idx = 0; idx < kMatrixSize; ++idx) { + info.mMatrix[idx / 2][idx % 2] = mFdp.ConsumeFloatingPoint<float>(); + } + info.mIsOpaque = mFdp.ConsumeBool(); + info.mContentDirty = mFdp.ConsumeBool(); + info.mStretchEffect.width = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.height = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.vectorX = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.vectorY = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.maxAmountX = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.maxAmountY = mFdp.ConsumeFloatingPoint<float>(); + info.mStretchEffect.mappedChildBounds = + FloatRect(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); + + Parcel parcel; + info.writeToParcel(&parcel); + parcel.setDataPosition(0); + info.readFromParcel(&parcel); +} + +void ParcelableFuzzer::invokeOccupancyTracker() { + nsecs_t totalTime = mFdp.ConsumeIntegral<uint32_t>(); + size_t numFrames = mFdp.ConsumeIntegral<size_t>(); + float occupancyAverage = mFdp.ConsumeFloatingPoint<float>(); + OccupancyTracker::Segment segment(totalTime, numFrames, occupancyAverage, + mFdp.ConsumeBool() /*usedThirdBuffer*/); + Parcel parcel; + segment.writeToParcel(&parcel); + parcel.setDataPosition(0); + segment.readFromParcel(&parcel); +} + +void ParcelableFuzzer::invokeStreamSplitter() { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + sp<StreamSplitter> splitter; + StreamSplitter::createSplitter(consumer, &splitter); + splitter->addOutput(producer); + std::string name = mFdp.ConsumeRandomLengthString(kMaxBytes); + splitter->setName(String8(name.c_str())); +} + +void ParcelableFuzzer::process() { + invokeStreamSplitter(); + invokeOccupancyTracker(); + invokeLayerDebugInfo(); + invokeLayerMetadata(); + invokeViewSurface(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + ParcelableFuzzer libGuiFuzzer(data, size); + libGuiFuzzer.process(); + return 0; +} diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp new file mode 100644 index 0000000000..57720dd513 --- /dev/null +++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp @@ -0,0 +1,327 @@ +/* + * Copyright 2022 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 <android/hardware/power/Boost.h> +#include <fuzzbinder/libbinder_driver.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <libgui_fuzzer_utils.h> +#include "android-base/stringprintf.h" + +using namespace android; + +constexpr int32_t kRandomStringMaxBytes = 256; + +constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, + ui::ColorMode::STANDARD_BT601_625, + ui::ColorMode::STANDARD_BT601_625_UNADJUSTED, + ui::ColorMode::STANDARD_BT601_525, + ui::ColorMode::STANDARD_BT601_525_UNADJUSTED, + ui::ColorMode::STANDARD_BT709, + ui::ColorMode::DCI_P3, + ui::ColorMode::SRGB, + ui::ColorMode::ADOBE_RGB, + ui::ColorMode::DISPLAY_P3, + ui::ColorMode::BT2020, + ui::ColorMode::BT2100_PQ, + ui::ColorMode::BT2100_HLG, + ui::ColorMode::DISPLAY_BT2020}; + +constexpr hardware::power::Boost kBoost[] = { + hardware::power::Boost::INTERACTION, hardware::power::Boost::DISPLAY_UPDATE_IMMINENT, + hardware::power::Boost::ML_ACC, hardware::power::Boost::AUDIO_LAUNCH, + hardware::power::Boost::CAMERA_LAUNCH, hardware::power::Boost::CAMERA_SHOT, +}; + +constexpr gui::TouchOcclusionMode kMode[] = { + gui::TouchOcclusionMode::BLOCK_UNTRUSTED, + gui::TouchOcclusionMode::USE_OPACITY, + gui::TouchOcclusionMode::ALLOW, +}; + +constexpr gui::WindowInfo::Flag kFlags[] = { + gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON, + gui::WindowInfo::Flag::DIM_BEHIND, + gui::WindowInfo::Flag::BLUR_BEHIND, + gui::WindowInfo::Flag::NOT_FOCUSABLE, + gui::WindowInfo::Flag::NOT_TOUCHABLE, + gui::WindowInfo::Flag::NOT_TOUCH_MODAL, + gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING, + gui::WindowInfo::Flag::KEEP_SCREEN_ON, + gui::WindowInfo::Flag::LAYOUT_IN_SCREEN, + gui::WindowInfo::Flag::LAYOUT_NO_LIMITS, + gui::WindowInfo::Flag::FULLSCREEN, + gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN, + gui::WindowInfo::Flag::DITHER, + gui::WindowInfo::Flag::SECURE, + gui::WindowInfo::Flag::SCALED, + gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES, + gui::WindowInfo::Flag::LAYOUT_INSET_DECOR, + gui::WindowInfo::Flag::ALT_FOCUSABLE_IM, + gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH, + gui::WindowInfo::Flag::SHOW_WHEN_LOCKED, + gui::WindowInfo::Flag::SHOW_WALLPAPER, + gui::WindowInfo::Flag::TURN_SCREEN_ON, + gui::WindowInfo::Flag::DISMISS_KEYGUARD, + gui::WindowInfo::Flag::SPLIT_TOUCH, + gui::WindowInfo::Flag::HARDWARE_ACCELERATED, + gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN, + gui::WindowInfo::Flag::TRANSLUCENT_STATUS, + gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION, + gui::WindowInfo::Flag::LOCAL_FOCUS_MODE, + gui::WindowInfo::Flag::SLIPPERY, + gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR, + gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS, +}; + +constexpr gui::WindowInfo::Type kType[] = { + gui::WindowInfo::Type::UNKNOWN, + gui::WindowInfo::Type::FIRST_APPLICATION_WINDOW, + gui::WindowInfo::Type::BASE_APPLICATION, + gui::WindowInfo::Type::APPLICATION, + gui::WindowInfo::Type::APPLICATION_STARTING, + gui::WindowInfo::Type::LAST_APPLICATION_WINDOW, + gui::WindowInfo::Type::FIRST_SUB_WINDOW, + gui::WindowInfo::Type::APPLICATION_PANEL, + gui::WindowInfo::Type::APPLICATION_MEDIA, + gui::WindowInfo::Type::APPLICATION_SUB_PANEL, + gui::WindowInfo::Type::APPLICATION_ATTACHED_DIALOG, + gui::WindowInfo::Type::APPLICATION_MEDIA_OVERLAY, +}; + +constexpr gui::WindowInfo::InputConfig kFeatures[] = { + gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL, + gui::WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, + gui::WindowInfo::InputConfig::DROP_INPUT, + gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, + gui::WindowInfo::InputConfig::SPY, + gui::WindowInfo::InputConfig::INTERCEPTS_STYLUS, +}; + +class SurfaceComposerClientFuzzer { +public: + SurfaceComposerClientFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + void invokeSurfaceComposerClient(); + void invokeSurfaceComposerClientBinder(); + void invokeSurfaceComposerTransaction(); + void getWindowInfo(gui::WindowInfo*); + sp<SurfaceControl> makeSurfaceControl(); + BlurRegion getBlurRegion(); + void fuzzOnPullAtom(); + gui::DisplayModeSpecs getDisplayModeSpecs(); + + FuzzedDataProvider mFdp; +}; + +gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() { + const auto getRefreshRateRange = [&] { + gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range; + range.min = mFdp.ConsumeFloatingPoint<float>(); + range.max = mFdp.ConsumeFloatingPoint<float>(); + return range; + }; + + const auto getRefreshRateRanges = [&] { + gui::DisplayModeSpecs::RefreshRateRanges ranges; + ranges.physical = getRefreshRateRange(); + ranges.render = getRefreshRateRange(); + return ranges; + }; + + String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); + sp<IBinder> displayToken = + SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); + gui::DisplayModeSpecs specs; + specs.defaultMode = mFdp.ConsumeIntegral<int32_t>(); + specs.allowGroupSwitching = mFdp.ConsumeBool(); + specs.primaryRanges = getRefreshRateRanges(); + specs.appRequestRanges = getRefreshRateRanges(); + return specs; +} + +BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() { + int32_t left = mFdp.ConsumeIntegral<int32_t>(); + int32_t right = mFdp.ConsumeIntegral<int32_t>(); + int32_t top = mFdp.ConsumeIntegral<int32_t>(); + int32_t bottom = mFdp.ConsumeIntegral<int32_t>(); + uint32_t blurRadius = mFdp.ConsumeIntegral<uint32_t>(); + float alpha = mFdp.ConsumeFloatingPoint<float>(); + float cornerRadiusTL = mFdp.ConsumeFloatingPoint<float>(); + float cornerRadiusTR = mFdp.ConsumeFloatingPoint<float>(); + float cornerRadiusBL = mFdp.ConsumeFloatingPoint<float>(); + float cornerRadiusBR = mFdp.ConsumeFloatingPoint<float>(); + return BlurRegion{blurRadius, cornerRadiusTL, cornerRadiusTR, cornerRadiusBL, + cornerRadiusBR, alpha, left, top, + right, bottom}; +} + +void SurfaceComposerClientFuzzer::getWindowInfo(gui::WindowInfo* windowInfo) { + windowInfo->id = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->name = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); + windowInfo->layoutParamsFlags = mFdp.PickValueInArray(kFlags); + windowInfo->layoutParamsType = mFdp.PickValueInArray(kType); + windowInfo->frameLeft = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->frameTop = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->frameRight = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->frameBottom = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->surfaceInset = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->alpha = mFdp.ConsumeFloatingPointInRange<float>(0, 1); + ui::Transform transform(mFdp.PickValueInArray(kOrientation)); + windowInfo->transform = transform; + windowInfo->touchableRegion = Region(getRect(&mFdp)); + windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool(); + windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode); + windowInfo->ownerPid = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->ownerUid = mFdp.ConsumeIntegral<int32_t>(); + windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); + windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures); +} + +sp<SurfaceControl> SurfaceComposerClientFuzzer::makeSurfaceControl() { + sp<IBinder> handle; + const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); + sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); + sp<BnGraphicBufferProducer> producer; + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t transformHint = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t flags = mFdp.ConsumeIntegral<uint32_t>(); + int32_t format = mFdp.ConsumeIntegral<int32_t>(); + int32_t layerId = mFdp.ConsumeIntegral<int32_t>(); + std::string layerName = base::StringPrintf("#%d", layerId); + return new SurfaceControl(client, handle, layerId, layerName, width, height, format, + transformHint, flags); +} + +void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() { + sp<SurfaceControl> surface = makeSurfaceControl(); + + SurfaceComposerClient::Transaction transaction; + int32_t layer = mFdp.ConsumeIntegral<int32_t>(); + transaction.setLayer(surface, layer); + + sp<SurfaceControl> relativeSurface = makeSurfaceControl(); + transaction.setRelativeLayer(surface, relativeSurface, layer); + + Region transparentRegion(getRect(&mFdp)); + transaction.setTransparentRegionHint(surface, transparentRegion); + transaction.setAlpha(surface, mFdp.ConsumeFloatingPoint<float>()); + + transaction.setCornerRadius(surface, mFdp.ConsumeFloatingPoint<float>()); + transaction.setBackgroundBlurRadius(surface, mFdp.ConsumeFloatingPoint<float>()); + std::vector<BlurRegion> regions; + uint32_t vectorSize = mFdp.ConsumeIntegralInRange<uint32_t>(0, 100); + regions.resize(vectorSize); + for (size_t idx = 0; idx < vectorSize; ++idx) { + regions.push_back(getBlurRegion()); + } + transaction.setBlurRegions(surface, regions); + + transaction.setLayerStack(surface, {mFdp.ConsumeIntegral<uint32_t>()}); + half3 color = {mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()}; + transaction.setColor(surface, color); + transaction.setBackgroundColor(surface, color, mFdp.ConsumeFloatingPoint<float>(), + mFdp.PickValueInArray(kDataspaces)); + + transaction.setApi(surface, mFdp.ConsumeIntegral<int32_t>()); + transaction.setFrameRateSelectionPriority(surface, mFdp.ConsumeIntegral<int32_t>()); + transaction.setColorSpaceAgnostic(surface, mFdp.ConsumeBool() /*agnostic*/); + + gui::WindowInfo windowInfo; + getWindowInfo(&windowInfo); + transaction.setInputWindowInfo(surface, windowInfo); + Parcel windowParcel; + windowInfo.writeToParcel(&windowParcel); + windowParcel.setDataPosition(0); + windowInfo.readFromParcel(&windowParcel); + + windowInfo.addTouchableRegion(getRect(&mFdp)); + int32_t pointX = mFdp.ConsumeIntegral<int32_t>(); + int32_t pointY = mFdp.ConsumeIntegral<int32_t>(); + windowInfo.touchableRegionContainsPoint(pointX, pointY); + windowInfo.frameContainsPoint(pointX, pointY); + + Parcel transactionParcel; + transaction.writeToParcel(&transactionParcel); + transactionParcel.setDataPosition(0); + transaction.readFromParcel(&transactionParcel); + SurfaceComposerClient::Transaction::createFromParcel(&transactionParcel); +} + +void SurfaceComposerClientFuzzer::fuzzOnPullAtom() { + std::string outData; + bool success; + SurfaceComposerClient::onPullAtom(mFdp.ConsumeIntegral<int32_t>(), &outData, &success); +} + +void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() { + String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); + sp<IBinder> displayToken = + SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); + SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs()); + + ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes); + SurfaceComposerClient::setActiveColorMode(displayToken, colorMode); + SurfaceComposerClient::setAutoLowLatencyMode(displayToken, mFdp.ConsumeBool() /*on*/); + SurfaceComposerClient::setGameContentType(displayToken, mFdp.ConsumeBool() /*on*/); + SurfaceComposerClient::setDisplayPowerMode(displayToken, mFdp.ConsumeIntegral<int32_t>()); + SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>()); + + SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp)); + hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost); + SurfaceComposerClient::notifyPowerBoost((int32_t)boostId); + + String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); + sp<BBinder> handle(new BBinder()); + sp<BnGraphicBufferProducer> producer; + sp<Surface> surfaceParent( + new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle)); + + fuzzOnPullAtom(); + SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken, + mFdp.ConsumeBool() /*enable*/, + mFdp.ConsumeIntegral<uint8_t>(), + mFdp.ConsumeIntegral<uint64_t>()); + + sp<IBinder> stopLayerHandle; + sp<gui::IRegionSamplingListener> listener = sp<gui::IRegionSamplingListenerDefault>::make(); + sp<gui::IRegionSamplingListenerDelegator> sampleListener = + new gui::IRegionSamplingListenerDelegator(listener); + SurfaceComposerClient::addRegionSamplingListener(getRect(&mFdp), stopLayerHandle, + sampleListener); + sp<gui::IFpsListenerDefault> fpsListener; + SurfaceComposerClient::addFpsListener(mFdp.ConsumeIntegral<int32_t>(), fpsListener); +} + +void SurfaceComposerClientFuzzer::invokeSurfaceComposerClientBinder() { + sp<FakeBnSurfaceComposerClient> client(new FakeBnSurfaceComposerClient()); + fuzzService(client.get(), std::move(mFdp)); +} + +void SurfaceComposerClientFuzzer::process() { + invokeSurfaceComposerClient(); + invokeSurfaceComposerTransaction(); + invokeSurfaceComposerClientBinder(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SurfaceComposerClientFuzzer surfaceComposerClientFuzzer(data, size); + surfaceComposerClientFuzzer.process(); + return 0; +} diff --git a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp new file mode 100644 index 0000000000..6d5427bc9e --- /dev/null +++ b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2021 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 <fuzzbinder/libbinder_driver.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <libgui_fuzzer_utils.h> + +using namespace android; + +class SurfaceComposerFuzzer { +public: + SurfaceComposerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + FuzzedDataProvider mFdp; +}; + +void SurfaceComposerFuzzer::process() { + sp<FakeBnSurfaceComposer> composer(new FakeBnSurfaceComposer()); + fuzzService(composer.get(), std::move(mFdp)); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SurfaceComposerFuzzer surfaceComposerFuzzer(data, size); + surfaceComposerFuzzer.process(); + return 0; +} diff --git a/libs/gui/include/gui/AidlStatusUtil.h b/libs/gui/include/gui/AidlStatusUtil.h new file mode 100644 index 0000000000..55be27bf35 --- /dev/null +++ b/libs/gui/include/gui/AidlStatusUtil.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 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/Status.h> + +// Extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h +namespace android::gui::aidl_utils { + +/** + * Return the equivalent Android status_t from a binder exception code. + * + * Generally one should use statusTFromBinderStatus() instead. + * + * Exception codes can be generated from a remote Java service exception, translate + * them for use on the Native side. + * + * Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code + * can be found from transactionError() or serviceSpecificErrorCode(). + */ +static inline status_t statusTFromExceptionCode(int32_t exceptionCode) { + using namespace ::android::binder; + switch (exceptionCode) { + case Status::EX_NONE: + return OK; + case Status::EX_SECURITY: // Java SecurityException, rethrows locally in Java + return PERMISSION_DENIED; + case Status::EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java + case Status::EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java + case Status::EX_NULL_POINTER: // Java NullPointerException, rethrows in Java + return BAD_VALUE; + case Status::EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java + case Status::EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows + return INVALID_OPERATION; + case Status::EX_HAS_REPLY_HEADER: // Native strictmode violation + case Status::EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows + case Status::EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows + case Status::EX_TRANSACTION_FAILED: // Native - see error code + case Status::EX_SERVICE_SPECIFIC: // Java ServiceSpecificException, + // rethrows in Java with integer error code + return UNKNOWN_ERROR; + } + return UNKNOWN_ERROR; +} + +/** + * Return the equivalent Android status_t from a binder status. + * + * Used to handle errors from a AIDL method declaration + * + * [oneway] void method(type0 param0, ...) + * + * or the following (where return_type is not a status_t) + * + * return_type method(type0 param0, ...) + */ +static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) { + return status.isOk() ? OK // check OK, + : status.serviceSpecificErrorCode() // service-side error, not standard Java exception + // (fromServiceSpecificError) + ?: status.transactionError() // a native binder transaction error (fromStatusT) + ?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a + // standard Java exception (fromExceptionCode) +} + +/** + * Return a binder::Status from native service status. + * + * This is used for methods not returning an explicit status_t, + * where Java callers expect an exception, not an integer return value. + */ +static inline ::android::binder::Status binderStatusFromStatusT( + status_t status, const char *optionalMessage = nullptr) { + const char *const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage; + // From binder::Status instructions: + // Prefer a generic exception code when possible, then a service specific + // code, and finally a status_t for low level failures or legacy support. + // Exception codes and service specific errors map to nicer exceptions for + // Java clients. + + using namespace ::android::binder; + switch (status) { + case OK: + return Status::ok(); + case PERMISSION_DENIED: // throw SecurityException on Java side + return Status::fromExceptionCode(Status::EX_SECURITY, emptyIfNull); + case BAD_VALUE: // throw IllegalArgumentException on Java side + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, emptyIfNull); + case INVALID_OPERATION: // throw IllegalStateException on Java side + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, emptyIfNull); + } + + // A service specific error will not show on status.transactionError() so + // be sure to use statusTFromBinderStatus() for reliable error handling. + + // throw a ServiceSpecificException. + return Status::fromServiceSpecificError(status, emptyIfNull); +} + +} // namespace android::gui::aidl_utils diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 48226b9641..47dcc42e16 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -69,9 +69,7 @@ private: bool mPreviouslyConnected GUARDED_BY(mMutex); }; -class BLASTBufferQueue - : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener -{ +class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener { public: BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, @@ -83,7 +81,6 @@ public: sp<Surface> getSurface(bool includeSurfaceControlHandle); bool isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const; - void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ } void onFrameReplaced(const BufferItem& item) override; void onFrameAvailable(const BufferItem& item) override; void onFrameDequeued(const uint64_t) override; @@ -115,15 +112,12 @@ public: uint32_t getLastTransformHint() const; uint64_t getLastAcquiredFrameNum(); - void abandon(); /** - * Set a callback to be invoked when we are hung. The boolean parameter - * indicates whether the hang is due to an unfired fence. - * TODO: The boolean is always true atm, unfired fence is - * the only case we detect. + * Set a callback to be invoked when we are hung. The string parameter + * indicates the reason for the hang. */ - void setTransactionHangCallback(std::function<void(bool)> callback); + void setTransactionHangCallback(std::function<void(const std::string&)> callback); virtual ~BLASTBufferQueue(); @@ -246,7 +240,7 @@ private: // Queues up transactions using this token in SurfaceFlinger. This prevents queued up // transactions from other parts of the client from blocking this transaction. - const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder(); + const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make(); // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or // we will deadlock. @@ -287,7 +281,7 @@ private: bool mAppliedLastTransaction = false; uint64_t mLastAppliedFrameNumber = 0; - std::function<void(bool)> mTransactionHangCallback; + std::function<void(const std::string&)> mTransactionHangCallback; std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); }; diff --git a/libs/gui/include/gui/CompositorTiming.h b/libs/gui/include/gui/CompositorTiming.h new file mode 100644 index 0000000000..cb8ca7a15c --- /dev/null +++ b/libs/gui/include/gui/CompositorTiming.h @@ -0,0 +1,43 @@ +/* + * Copyright 2022 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 <utils/Timers.h> + +namespace android::gui { + +// Expected timing of the next composited frame, based on the timing of the latest frames. +struct CompositorTiming { + static constexpr nsecs_t kDefaultVsyncPeriod = 16'666'667; + + CompositorTiming() = default; + CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase, + nsecs_t presentLatency); + + // Time point when compositing is expected to start. + nsecs_t deadline = 0; + + // Duration between consecutive frames. In other words, the VSYNC period. + nsecs_t interval = kDefaultVsyncPeriod; + + // Duration between composite start and present. For missed frames, the extra latency is rounded + // to a multiple of the VSYNC period, such that the remainder (presentLatency % interval) always + // evaluates to the VSYNC phase offset. + nsecs_t presentLatency = kDefaultVsyncPeriod; +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index a3425395bf..bf3a07b6b5 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -23,10 +23,10 @@ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; class DisplayEventDispatcher : public LooperCallback { public: - explicit DisplayEventDispatcher( - const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); + explicit DisplayEventDispatcher(const sp<Looper>& looper, + gui::ISurfaceComposer::VsyncSource vsyncSource = + gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, + EventRegistrationFlags eventRegistration = {}); status_t initialize(); void dispose(); diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index cf7a4e5522..0f4907fcb9 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -20,20 +20,26 @@ #include <stdint.h> #include <sys/types.h> +#include <ftl/flags.h> + #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Timers.h> +#include <android/gui/ISurfaceComposer.h> #include <binder/IInterface.h> -#include <gui/ISurfaceComposer.h> #include <gui/VsyncEventData.h> +#include <ui/DisplayId.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- +using EventRegistrationFlags = ftl::Flags<gui::ISurfaceComposer::EventRegistration>; + using gui::IDisplayEventConnection; using gui::ParcelableVsyncEventData; using gui::VsyncEventData; @@ -111,9 +117,9 @@ public: * To receive ModeChanged and/or FrameRateOverrides events specify this in * the constructor. Other events start being delivered immediately. */ - explicit DisplayEventReceiver( - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); + explicit DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource vsyncSource = + gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, + EventRegistrationFlags eventRegistration = {}); /* * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h index 74f33a2a87..42b62c755c 100644 --- a/libs/gui/include/gui/DisplayInfo.h +++ b/libs/gui/include/gui/DisplayInfo.h @@ -41,6 +41,8 @@ struct DisplayInfo : public Parcelable { status_t writeToParcel(android::Parcel*) const override; status_t readFromParcel(const android::Parcel*) override; + + void dump(std::string& result, const char* prefix = "") const; }; } // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index dd3de58844..c08a9b1b6c 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -17,6 +17,9 @@ #ifndef ANDROID_GUI_FRAMETIMESTAMPS_H #define ANDROID_GUI_FRAMETIMESTAMPS_H +#include <android/gui/FrameEvent.h> + +#include <gui/CompositorTiming.h> #include <ui/FenceTime.h> #include <utils/Flattenable.h> #include <utils/StrongPointer.h> @@ -31,22 +34,8 @@ namespace android { struct FrameEvents; class FrameEventHistoryDelta; - -// Identifiers for all the events that may be recorded or reported. -enum class FrameEvent { - POSTED, - REQUESTED_PRESENT, - LATCH, - ACQUIRE, - FIRST_REFRESH_START, - LAST_REFRESH_START, - GPU_COMPOSITION_DONE, - DISPLAY_PRESENT, - DEQUEUE_READY, - RELEASE, - EVENT_COUNT, // Not an actual event. -}; - +using gui::CompositorTiming; +using gui::FrameEvent; // A collection of timestamps corresponding to a single frame. struct FrameEvents { @@ -96,12 +85,6 @@ struct FrameEvents { std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE}; }; -struct CompositorTiming { - nsecs_t deadline{0}; - nsecs_t interval{16666667}; - nsecs_t presentLatency{16666667}; -}; - // A short history of frames that are synchronized between the consumer and // producer via deltas. class FrameEventHistory { diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 2f538ffb86..ba268ab17a 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -138,6 +138,10 @@ public: const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform, bool filtering); + static void computeTransformMatrix(float outTransform[16], float bufferWidth, + float bufferHeight, PixelFormat pixelFormat, + const Rect& cropRect, uint32_t transform, bool filtering); + // Scale the crop down horizontally or vertically such that it has the // same aspect ratio as the buffer does. static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index a610e940be..d70a7f0f1b 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -17,19 +17,17 @@ #pragma once #include <android/gui/DisplayBrightness.h> +#include <android/gui/FrameTimelineInfo.h> #include <android/gui/IDisplayEventConnection.h> #include <android/gui/IFpsListener.h> #include <android/gui/IHdrLayerInfoListener.h> #include <android/gui/IRegionSamplingListener.h> #include <android/gui/IScreenCaptureListener.h> -#include <android/gui/ITransactionTraceListener.h> +#include <android/gui/ITransactionCompletedListener.h> #include <android/gui/ITunnelModeEnabledListener.h> #include <android/gui/IWindowInfosListener.h> #include <binder/IBinder.h> #include <binder/IInterface.h> -#include <ftl/flags.h> -#include <gui/FrameTimelineInfo.h> -#include <gui/ITransactionCompletedListener.h> #include <gui/SpHash.h> #include <math/vec4.h> #include <stdint.h> @@ -57,26 +55,25 @@ namespace android { struct client_cache_t; -struct ComposerState; +class ComposerState; struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; -class LayerDebugInfo; class HdrCapabilities; -class IGraphicBufferProducer; -class ISurfaceComposerClient; class Rect; -enum class FrameEvent; +using gui::FrameTimelineInfo; using gui::IDisplayEventConnection; using gui::IRegionSamplingListener; using gui::IScreenCaptureListener; +using gui::ListenerCallbacks; using gui::SpHash; namespace gui { struct DisplayCaptureArgs; struct LayerCaptureArgs; +class LayerDebugInfo; } // namespace gui @@ -85,7 +82,6 @@ namespace ui { struct DisplayMode; struct DisplayState; struct DynamicDisplayInfo; -struct StaticDisplayInfo; } // namespace ui @@ -97,11 +93,8 @@ class ISurfaceComposer: public IInterface { public: DECLARE_META_INTERFACE(SurfaceComposer) - static constexpr size_t MAX_LAYERS = 4096; - // flags for setTransactionState() enum { - eSynchronous = 0x01, eAnimation = 0x02, // Explicit indication that this transaction and others to follow will likely result in a @@ -116,322 +109,13 @@ public: eOneWay = 0x20 }; - enum VsyncSource { - eVsyncSourceApp = 0, - eVsyncSourceSurfaceFlinger = 1 - }; - - enum class EventRegistration { - modeChanged = 1 << 0, - frameRateOverride = 1 << 1, - }; - - using EventRegistrationFlags = ftl::Flags<EventRegistration>; - - /* - * Create a connection with SurfaceFlinger. - */ - virtual sp<ISurfaceComposerClient> createConnection() = 0; - - /* return an IDisplayEventConnection */ - virtual sp<IDisplayEventConnection> createDisplayEventConnection( - VsyncSource vsyncSource = eVsyncSourceApp, - EventRegistrationFlags eventRegistration = {}) = 0; - /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ virtual status_t setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state, + const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0; - - /* signal that we're done booting. - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual void bootFinished() = 0; - - /* verify that an IGraphicBufferProducer was created by SurfaceFlinger. - */ - virtual bool authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& surface) const = 0; - - /* Returns the frame timestamps supported by SurfaceFlinger. - */ - virtual status_t getSupportedFrameTimestamps( - std::vector<FrameEvent>* outSupported) const = 0; - - /** - * Gets immutable information about given physical display. - */ - virtual status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*) = 0; - - /** - * Gets dynamic information about given physical display. - */ - virtual status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*) = 0; - - virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display, - ui::DisplayPrimaries& primaries) = 0; - virtual status_t setActiveColorMode(const sp<IBinder>& display, - ui::ColorMode colorMode) = 0; - - /** - * Sets the user-preferred display mode that a device should boot in. - */ - virtual status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId) = 0; - - /* Clears the frame statistics for animations. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t clearAnimationFrameStats() = 0; - - /* Gets the frame statistics for animations. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0; - - /* Overrides the supported HDR modes for the given display device. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t overrideHdrTypes(const sp<IBinder>& display, - const std::vector<ui::Hdr>& hdrTypes) = 0; - - /* Pulls surfaceflinger atoms global stats and layer stats to pipe to statsd. - * - * Requires the calling uid be from system server. - */ - virtual status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success) = 0; - - virtual status_t enableVSyncInjections(bool enable) = 0; - - virtual status_t injectVSync(nsecs_t when) = 0; - - /* Gets the list of active layers in Z order for debugging purposes - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) = 0; - - virtual status_t getColorManagement(bool* outGetColorManagement) const = 0; - - /* Gets the composition preference of the default data space and default pixel format, - * as well as the wide color gamut data space and wide color gamut pixel format. - * If the wide color gamut data space is V0_SRGB, then it implies that the platform - * has no wide color gamut support. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, - ui::PixelFormat* defaultPixelFormat, - ui::Dataspace* wideColorGamutDataspace, - ui::PixelFormat* wideColorGamutPixelFormat) const = 0; - /* - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, - ui::PixelFormat* outFormat, - ui::Dataspace* outDataspace, - uint8_t* outComponentMask) const = 0; - - /* Turns on the color sampling engine on the display. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, - uint8_t componentMask, - uint64_t maxFrames) = 0; - - /* Returns statistics on the color profile of the last frame displayed for a given display - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, - uint64_t timestamp, - DisplayedFrameStats* outStats) const = 0; - - /* - * Gets whether SurfaceFlinger can support protected content in GPU composition. - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getProtectedContentSupport(bool* outSupported) const = 0; - - /* Registers a listener to stream median luma updates from SurfaceFlinger. - * - * The sampling area is bounded by both samplingArea and the given stopLayerHandle - * (i.e., only layers behind the stop layer will be captured and sampled). - * - * Multiple listeners may be provided so long as they have independent listeners. - * If multiple listeners are provided, the effective sampling region for each listener will - * be bounded by whichever stop layer has a lower Z value. - * - * Requires the same permissions as captureLayers and captureScreen. - */ - virtual status_t addRegionSamplingListener(const Rect& samplingArea, - const sp<IBinder>& stopLayerHandle, - const sp<IRegionSamplingListener>& listener) = 0; - - /* - * Removes a listener that was streaming median luma updates from SurfaceFlinger. - */ - virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0; - - /* Registers a listener that streams fps updates from SurfaceFlinger. - * - * The listener will stream fps updates for the layer tree rooted at the layer denoted by the - * task ID, i.e., the layer must have the task ID as part of its layer metadata with key - * METADATA_TASK_ID. If there is no such layer, then no fps is expected to be reported. - * - * Multiple listeners may be supported. - * - * Requires the READ_FRAME_BUFFER permission. - */ - virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) = 0; - /* - * Removes a listener that was streaming fps updates from SurfaceFlinger. - */ - virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0; - - /* Registers a listener to receive tunnel mode enabled updates from SurfaceFlinger. - * - * Requires ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t addTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) = 0; - - /* - * Removes a listener that was receiving tunnel mode enabled updates from SurfaceFlinger. - * - * Requires ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t removeTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) = 0; - - /* Sets the refresh rate boundaries for the display. - * - * The primary refresh rate range represents display manager's general guidance on the display - * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an - * app, we should stay within this range. - * - * The app request refresh rate range allows us to consider more display modes when switching - * refresh rates. Although we should generally stay within the primary range, specific - * considerations, such as layer frame rate settings specified via the setFrameRate() api, may - * cause us to go outside the primary range. We never go outside the app request range. The app - * request range will be greater than or equal to the primary refresh rate range, never smaller. - * - * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider - * switching between. Only modes with a mode group and resolution matching defaultMode - * will be considered for switching. The defaultMode corresponds to an ID of mode in the list - * of supported modes returned from getDynamicDisplayInfo(). - */ - virtual status_t setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, float appRequestRefreshRateMax) = 0; - - virtual status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) = 0; - - /* - * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows - * material design guidelines. - * - * ambientColor - * Color to the ambient shadow. The alpha is premultiplied. - * - * spotColor - * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow - * depends on the light position. - * - * lightPosY/lightPosZ - * Position of the light used to cast the spot shadow. The X value is always the display - * width / 2. - * - * lightRadius - * Radius of the light casting the shadow. - */ - virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, - float lightPosY, float lightPosZ, - float lightRadius) = 0; - - /* - * Gets whether a display supports DISPLAY_DECORATION layers. - * - * displayToken - * The token of the display. - * outSupport - * An output parameter for whether/how the display supports - * DISPLAY_DECORATION layers. - * - * 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 getDisplayDecorationSupport( - const sp<IBinder>& displayToken, - std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* - outSupport) const = 0; - - /* - * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info. - */ - virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility, int8_t changeFrameRateStrategy) = 0; - - /* - * Set the override frame rate for a specified uid by GameManagerService. - * Passing the frame rate and uid to SurfaceFlinger to update the override mapping - * in the scheduler. - */ - virtual status_t setOverrideFrameRate(uid_t uid, float frameRate) = 0; - - /* - * Sets the frame timeline vsync info received from choreographer that corresponds to next - * buffer submitted on that surface. - */ - virtual status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, - const FrameTimelineInfo& frameTimelineInfo) = 0; - - /* - * Adds a TransactionTraceListener to listen for transaction tracing state updates. - */ - virtual status_t addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) = 0; - - /** - * Gets priority of the RenderEngine in SurfaceFlinger. - */ - virtual int getGPUContextPriority() = 0; - - /** - * Gets the number of buffers SurfaceFlinger would need acquire. This number - * would be propagated to the client via MIN_UNDEQUEUED_BUFFERS so that the - * client could allocate enough buffers to match SF expectations of the - * pipeline depth. SurfaceFlinger will make sure that it will give the app at - * least the time configured as the 'appDuration' before trying to latch - * the buffer. - * - * The total buffers needed for a given configuration is basically the - * numbers of vsyncs a single buffer is used across the stack. For the default - * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger - * and 1 vsync by the display. The extra buffers are calculated as the - * number of additional buffers on top of the 2 buffers already present - * in MIN_UNDEQUEUED_BUFFERS. - */ - virtual status_t getMaxAcquiredBufferCount(int* buffers) const = 0; - - virtual status_t addWindowInfosListener( - const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0; - virtual status_t removeWindowInfosListener( - const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0; }; // ---------------------------------------------------------------------------- @@ -442,77 +126,77 @@ public: // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, - CREATE_CONNECTION, - GET_STATIC_DISPLAY_INFO, - CREATE_DISPLAY_EVENT_CONNECTION, - CREATE_DISPLAY, // Deprecated. Autogenerated by .aidl now. - DESTROY_DISPLAY, // Deprecated. Autogenerated by .aidl now. - GET_PHYSICAL_DISPLAY_TOKEN, // Deprecated. Autogenerated by .aidl now. + CREATE_CONNECTION, // Deprecated. Autogenerated by .aidl now. + GET_STATIC_DISPLAY_INFO, // Deprecated. Autogenerated by .aidl now. + CREATE_DISPLAY_EVENT_CONNECTION, // Deprecated. Autogenerated by .aidl now. + CREATE_DISPLAY, // Deprecated. Autogenerated by .aidl now. + DESTROY_DISPLAY, // Deprecated. Autogenerated by .aidl now. + GET_PHYSICAL_DISPLAY_TOKEN, // Deprecated. Autogenerated by .aidl now. SET_TRANSACTION_STATE, - AUTHENTICATE_SURFACE, - GET_SUPPORTED_FRAME_TIMESTAMPS, - GET_DISPLAY_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. - GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + AUTHENTICATE_SURFACE, // Deprecated. Autogenerated by .aidl now. + GET_SUPPORTED_FRAME_TIMESTAMPS, // Deprecated. Autogenerated by .aidl now. + GET_DISPLAY_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. GET_DISPLAY_STATE, - CAPTURE_DISPLAY, // Deprecated. Autogenerated by .aidl now. - CAPTURE_LAYERS, // Deprecated. Autogenerated by .aidl now. - CLEAR_ANIMATION_FRAME_STATS, - GET_ANIMATION_FRAME_STATS, - SET_POWER_MODE, // Deprecated. Autogenerated by .aidl now. + CAPTURE_DISPLAY, // Deprecated. Autogenerated by .aidl now. + CAPTURE_LAYERS, // Deprecated. Autogenerated by .aidl now. + CLEAR_ANIMATION_FRAME_STATS, // Deprecated. Autogenerated by .aidl now. + GET_ANIMATION_FRAME_STATS, // Deprecated. Autogenerated by .aidl now. + SET_POWER_MODE, // Deprecated. Autogenerated by .aidl now. GET_DISPLAY_STATS, - GET_HDR_CAPABILITIES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. - GET_DISPLAY_COLOR_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. - GET_ACTIVE_COLOR_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. - SET_ACTIVE_COLOR_MODE, - ENABLE_VSYNC_INJECTIONS, - INJECT_VSYNC, - GET_LAYER_DEBUG_INFO, - GET_COMPOSITION_PREFERENCE, - GET_COLOR_MANAGEMENT, - GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, - SET_DISPLAY_CONTENT_SAMPLING_ENABLED, + GET_HDR_CAPABILITIES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_DISPLAY_COLOR_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_ACTIVE_COLOR_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + SET_ACTIVE_COLOR_MODE, // Deprecated. Autogenerated by .aidl now. + ENABLE_VSYNC_INJECTIONS, // Deprecated. Autogenerated by .aidl now. + INJECT_VSYNC, // Deprecated. Autogenerated by .aidl now. + GET_LAYER_DEBUG_INFO, // Deprecated. Autogenerated by .aidl now. + GET_COMPOSITION_PREFERENCE, // Deprecated. Autogenerated by .aidl now. + GET_COLOR_MANAGEMENT, // Deprecated. Autogenerated by .aidl now. + GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, // Deprecated. Autogenerated by .aidl now. + SET_DISPLAY_CONTENT_SAMPLING_ENABLED, // Deprecated. Autogenerated by .aidl now. GET_DISPLAYED_CONTENT_SAMPLE, - GET_PROTECTED_CONTENT_SUPPORT, - IS_WIDE_COLOR_DISPLAY, // Deprecated. Autogenerated by .aidl now. - GET_DISPLAY_NATIVE_PRIMARIES, - GET_PHYSICAL_DISPLAY_IDS, // Deprecated. Autogenerated by .aidl now. - ADD_REGION_SAMPLING_LISTENER, - REMOVE_REGION_SAMPLING_LISTENER, - SET_DESIRED_DISPLAY_MODE_SPECS, - GET_DESIRED_DISPLAY_MODE_SPECS, - GET_DISPLAY_BRIGHTNESS_SUPPORT, // Deprecated. Autogenerated by .aidl now. - SET_DISPLAY_BRIGHTNESS, // Deprecated. Autogenerated by .aidl now. - CAPTURE_DISPLAY_BY_ID, // Deprecated. Autogenerated by .aidl now. - NOTIFY_POWER_BOOST, // Deprecated. Autogenerated by .aidl now. + GET_PROTECTED_CONTENT_SUPPORT, // Deprecated. Autogenerated by .aidl now. + IS_WIDE_COLOR_DISPLAY, // Deprecated. Autogenerated by .aidl now. + GET_DISPLAY_NATIVE_PRIMARIES, // Deprecated. Autogenerated by .aidl now. + GET_PHYSICAL_DISPLAY_IDS, // Deprecated. Autogenerated by .aidl now. + ADD_REGION_SAMPLING_LISTENER, // Deprecated. Autogenerated by .aidl now. + REMOVE_REGION_SAMPLING_LISTENER, // Deprecated. Autogenerated by .aidl now. + SET_DESIRED_DISPLAY_MODE_SPECS, // Deprecated. Autogenerated by .aidl now. + GET_DESIRED_DISPLAY_MODE_SPECS, // Deprecated. Autogenerated by .aidl now. + GET_DISPLAY_BRIGHTNESS_SUPPORT, // Deprecated. Autogenerated by .aidl now. + SET_DISPLAY_BRIGHTNESS, // Deprecated. Autogenerated by .aidl now. + CAPTURE_DISPLAY_BY_ID, // Deprecated. Autogenerated by .aidl now. + NOTIFY_POWER_BOOST, // Deprecated. Autogenerated by .aidl now. SET_GLOBAL_SHADOW_SETTINGS, GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_AUTO_LOW_LATENCY_MODE, // Deprecated. Autogenerated by .aidl now. GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_GAME_CONTENT_TYPE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. - SET_FRAME_RATE, + SET_FRAME_RATE, // Deprecated. Autogenerated by .aidl now. // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true); ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, - SET_FRAME_TIMELINE_INFO, - ADD_TRANSACTION_TRACE_LISTENER, + SET_FRAME_TIMELINE_INFO, // Deprecated. Autogenerated by .aidl now. + ADD_TRANSACTION_TRACE_LISTENER, // Deprecated. Autogenerated by .aidl now. GET_GPU_CONTEXT_PRIORITY, GET_MAX_ACQUIRED_BUFFER_COUNT, - GET_DYNAMIC_DISPLAY_INFO, - ADD_FPS_LISTENER, - REMOVE_FPS_LISTENER, - OVERRIDE_HDR_TYPES, - ADD_HDR_LAYER_INFO_LISTENER, // Deprecated. Autogenerated by .aidl now. - REMOVE_HDR_LAYER_INFO_LISTENER, // Deprecated. Autogenerated by .aidl now. - ON_PULL_ATOM, - ADD_TUNNEL_MODE_ENABLED_LISTENER, - REMOVE_TUNNEL_MODE_ENABLED_LISTENER, - ADD_WINDOW_INFOS_LISTENER, - REMOVE_WINDOW_INFOS_LISTENER, - GET_PRIMARY_PHYSICAL_DISPLAY_ID, // Deprecated. Autogenerated by .aidl now. + GET_DYNAMIC_DISPLAY_INFO, // Deprecated. Autogenerated by .aidl now. + ADD_FPS_LISTENER, // Deprecated. Autogenerated by .aidl now. + REMOVE_FPS_LISTENER, // Deprecated. Autogenerated by .aidl now. + OVERRIDE_HDR_TYPES, // Deprecated. Autogenerated by .aidl now. + ADD_HDR_LAYER_INFO_LISTENER, // Deprecated. Autogenerated by .aidl now. + REMOVE_HDR_LAYER_INFO_LISTENER, // Deprecated. Autogenerated by .aidl now. + ON_PULL_ATOM, // Deprecated. Autogenerated by .aidl now. + ADD_TUNNEL_MODE_ENABLED_LISTENER, // Deprecated. Autogenerated by .aidl now. + REMOVE_TUNNEL_MODE_ENABLED_LISTENER, // Deprecated. Autogenerated by .aidl now. + ADD_WINDOW_INFOS_LISTENER, // Deprecated. Autogenerated by .aidl now. + REMOVE_WINDOW_INFOS_LISTENER, // Deprecated. Autogenerated by .aidl now. + GET_PRIMARY_PHYSICAL_DISPLAY_ID, // Deprecated. Autogenerated by .aidl now. GET_DISPLAY_DECORATION_SUPPORT, GET_BOOT_DISPLAY_MODE_SUPPORT, // Deprecated. Autogenerated by .aidl now. - SET_BOOT_DISPLAY_MODE, - CLEAR_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now. - SET_OVERRIDE_FRAME_RATE, + SET_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now. + CLEAR_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now. + SET_OVERRIDE_FRAME_RATE, // Deprecated. Autogenerated by .aidl now. // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h deleted file mode 100644 index 9e9e191480..0000000000 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2007 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/SafeInterface.h> -#include <gui/LayerMetadata.h> -#include <ui/PixelFormat.h> - -#include <unordered_map> - -namespace android { - -class FrameStats; -class IGraphicBufferProducer; - -class ISurfaceComposerClient : public IInterface { -public: - DECLARE_META_INTERFACE(SurfaceComposerClient) - - // flags for createSurface() - enum { // (keep in sync with SurfaceControl.java) - eHidden = 0x00000004, - eDestroyBackbuffer = 0x00000020, - eSkipScreenshot = 0x00000040, - eSecure = 0x00000080, - eNonPremultiplied = 0x00000100, - eOpaque = 0x00000400, - eProtectedByApp = 0x00000800, - eProtectedByDRM = 0x00001000, - eCursorWindow = 0x00002000, - eNoColorFill = 0x00004000, - - eFXSurfaceBufferQueue = 0x00000000, - eFXSurfaceEffect = 0x00020000, - eFXSurfaceBufferState = 0x00040000, - eFXSurfaceContainer = 0x00080000, - eFXSurfaceMask = 0x000F0000, - }; - - // TODO(b/172002646): Clean up the Surface Creation Arguments - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags, const sp<IBinder>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint) = 0; - - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, - PixelFormat format, uint32_t flags, - const sp<IGraphicBufferProducer>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint) = 0; - - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const = 0; - - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0; - - virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, - int32_t* outLayerId) = 0; -}; - -class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> { -public: - BnSurfaceComposerClient() - : SafeBnInterface<ISurfaceComposerClient>("BnSurfaceComposerClient") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; -}; - -} // namespace android diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h index ce9716f1fe..1dddeba616 100644 --- a/libs/gui/include/gui/JankInfo.h +++ b/libs/gui/include/gui/JankInfo.h @@ -24,9 +24,9 @@ enum JankType { None = 0x0, // Jank that occurs in the layers below SurfaceFlinger DisplayHAL = 0x1, - // SF took too long on the CPU + // SF took too long on the CPU; deadline missed during HWC SurfaceFlingerCpuDeadlineMissed = 0x2, - // SF took too long on the GPU + // SF took too long on the GPU; deadline missed during GPU composition SurfaceFlingerGpuDeadlineMissed = 0x4, // Either App or GPU took too long on the frame AppDeadlineMissed = 0x8, diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index af834d78df..dbb80e583c 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -25,7 +25,7 @@ #include <string> #include <math/vec4.h> -namespace android { +namespace android::gui { /* Class for transporting debug info from SurfaceFlinger to authorized * recipients. The class is intended to be a data container. There are @@ -52,7 +52,7 @@ public: uint32_t mZ = 0 ; int32_t mWidth = -1; int32_t mHeight = -1; - Rect mCrop = Rect::INVALID_RECT; + android::Rect mCrop = android::Rect::INVALID_RECT; half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf); uint32_t mFlags = 0; PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; @@ -71,4 +71,4 @@ public: std::string to_string(const LayerDebugInfo& info); -} // namespace android +} // namespace android::gui diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 27f4d379e9..9cf62bc7d6 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -20,7 +20,7 @@ #include <unordered_map> -namespace android { +namespace android::gui { enum { METADATA_OWNER_UID = 1, @@ -30,7 +30,8 @@ enum { METADATA_ACCESSIBILITY_ID = 5, METADATA_OWNER_PID = 6, METADATA_DEQUEUE_TIME = 7, - METADATA_GAME_MODE = 8 + METADATA_GAME_MODE = 8, + METADATA_CALLING_UID = 9, }; struct LayerMetadata : public Parcelable { @@ -65,8 +66,18 @@ enum class GameMode : int32_t { Standard = 1, Performance = 2, Battery = 3, + Custom = 4, - ftl_last = Battery + ftl_last = Custom }; -} // namespace android +} // namespace android::gui + +using android::gui::METADATA_ACCESSIBILITY_ID; +using android::gui::METADATA_DEQUEUE_TIME; +using android::gui::METADATA_GAME_MODE; +using android::gui::METADATA_MOUSE_CURSOR; +using android::gui::METADATA_OWNER_PID; +using android::gui::METADATA_OWNER_UID; +using android::gui::METADATA_TASK_ID; +using android::gui::METADATA_WINDOW_TYPE; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 0a9b75a7f1..c5fdf82d4f 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -21,9 +21,10 @@ #include <stdint.h> #include <sys/types.h> +#include <android/gui/ITransactionCompletedListener.h> +#include <android/gui/IWindowInfosReportedListener.h> #include <android/native_window.h> #include <gui/IGraphicBufferProducer.h> -#include <gui/ITransactionCompletedListener.h> #include <math/mat4.h> #include <android/gui/DropInputMode.h> @@ -34,6 +35,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/LayerCaptureArgs.h> #include <gui/LayerMetadata.h> +#include <gui/ReleaseCallbackId.h> #include <gui/SpHash.h> #include <gui/SurfaceControl.h> #include <gui/WindowInfo.h> @@ -51,7 +53,12 @@ namespace android { class Parcel; -class ISurfaceComposerClient; + +using gui::ISurfaceComposerClient; +using gui::LayerMetadata; + +using gui::ITransactionCompletedListener; +using gui::ReleaseCallbackId; struct client_cache_t { wp<IBinder> token = nullptr; @@ -130,7 +137,7 @@ struct layer_state_t { eLayerOpaque = 0x02, // SURFACE_OPAQUE eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT eLayerSecure = 0x80, // SECURE - // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is + // Queue up layer buffers instead of dropping the oldest buffer when this flag is // set. This blocks the client until all the buffers have been presented. If the buffers // have presentation timestamps, then we may drop buffers. eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE @@ -145,25 +152,27 @@ struct layer_state_t { enum { ePositionChanged = 0x00000001, eLayerChanged = 0x00000002, - eSizeChanged = 0x00000004, + /* unused = 0x00000004, */ eAlphaChanged = 0x00000008, eMatrixChanged = 0x00000010, eTransparentRegionChanged = 0x00000020, eFlagsChanged = 0x00000040, eLayerStackChanged = 0x00000080, + /* unused = 0x00000100, */ + /* unused = 0x00000200, */ eDimmingEnabledChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, - /* unused 0x00001000, */ + eRenderBorderChanged = 0x00001000, eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, eColorChanged = 0x00010000, - eDestroySurface = 0x00020000, - eTransformChanged = 0x00040000, + /* unused = 0x00020000, */ + eBufferTransformChanged = 0x00040000, eTransformToDisplayInverseChanged = 0x00080000, eCropChanged = 0x00100000, eBufferChanged = 0x00200000, - /* unused 0x00400000, */ + eDefaultFrameRateCompatibilityChanged = 0x00400000, eDataspaceChanged = 0x00800000, eHdrMetadataChanged = 0x01000000, eSurfaceDamageRegionChanged = 0x02000000, @@ -196,7 +205,32 @@ struct layer_state_t { void merge(const layer_state_t& other); status_t write(Parcel& output) const; status_t read(const Parcel& input); + // Compares two layer_state_t structs and returns a set of change flags describing all the + // states that are different. + uint64_t diff(const layer_state_t& other) const; bool hasBufferChanges() const; + + // Changes to the tree structure. + static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged | + layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent | + layer_state_t::eBackgroundColorChanged; + // Content updates. + static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged | + layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged | + layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged | + layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | + layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged | + layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | + layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged | + layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged | + layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged; + // Changes to content or children size. + static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged | + layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged | + layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged | + layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged | + layer_state_t::eDestinationFrameChanged; + bool hasValidBuffer() const; void sanitize(int32_t permissions); @@ -207,6 +241,11 @@ struct layer_state_t { float dsdy{0}; status_t write(Parcel& output) const; status_t read(const Parcel& input); + inline bool operator==(const matrix22_t& other) const { + return std::tie(dsdx, dtdx, dtdy, dsdy) == + std::tie(other.dsdx, other.dtdx, other.dtdy, other.dsdy); + } + inline bool operator!=(const matrix22_t& other) const { return !(*this == other); } }; sp<IBinder> surface; int32_t layerId; @@ -214,28 +253,23 @@ struct layer_state_t { float x; float y; int32_t z; - uint32_t w; - uint32_t h; ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; - float alpha; uint32_t flags; uint32_t mask; uint8_t reserved; matrix22_t matrix; float cornerRadius; uint32_t backgroundBlurRadius; - sp<SurfaceControl> reparentSurfaceControl; sp<SurfaceControl> relativeLayerSurfaceControl; sp<SurfaceControl> parentSurfaceControlForChild; - half3 color; + half4 color; // non POD must be last. see write/read Region transparentRegion; - - uint32_t transform; + uint32_t bufferTransform; bool transformToDisplayInverse; Rect crop; std::shared_ptr<BufferData> bufferData = nullptr; @@ -247,7 +281,7 @@ struct layer_state_t { mat4 colorTransform; std::vector<BlurRegion> blurRegions; - sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle(); + sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make(); LayerMetadata metadata; @@ -273,6 +307,9 @@ struct layer_state_t { int8_t frameRateCompatibility; int8_t changeFrameRateStrategy; + // Default frame rate compatibility used to set the layer refresh rate votetype. + int8_t defaultFrameRateCompatibility; + // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that // the graphic producers should receive a transform hint as if the @@ -291,6 +328,11 @@ struct layer_state_t { // should be trusted for input occlusion detection purposes bool isTrustedOverlay; + // Flag to indicate if border needs to be enabled on the layer + bool borderEnabled; + float borderWidth; + half4 borderColor; + // Stretch effect to be applied to this layer StretchEffect stretchEffect; @@ -303,7 +345,8 @@ struct layer_state_t { bool dimmingEnabled; }; -struct ComposerState { +class ComposerState { +public: layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); @@ -320,6 +363,7 @@ struct DisplayState { DisplayState(); void merge(const DisplayState& other); + void sanitize(int32_t permissions); uint32_t what = 0; uint32_t flags = 0; @@ -352,7 +396,9 @@ struct DisplayState { struct InputWindowCommands { std::vector<gui::FocusRequest> focusRequests; - bool syncInputWindows{false}; + std::unordered_set<sp<gui::IWindowInfosReportedListener>, + SpHash<gui::IWindowInfosReportedListener>> + windowInfosReportedListeners; // Merges the passed in commands and returns true if there were any changes. bool merge(const InputWindowCommands& other); diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h index cc136bb40a..3a12802146 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ListenerStats.h @@ -24,6 +24,8 @@ #include <binder/SafeInterface.h> #include <gui/FrameTimestamps.h> +#include <gui/ReleaseCallbackId.h> + #include <ui/Fence.h> #include <utils/Timers.h> @@ -32,10 +34,7 @@ #include <unordered_set> #include <variant> -namespace android { - -class ITransactionCompletedListener; -class ListenerCallbacks; +namespace android::gui { class CallbackId : public Parcelable { public: @@ -54,30 +53,6 @@ struct CallbackIdHash { std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); } }; -class ReleaseCallbackId : public Parcelable { -public: - static const ReleaseCallbackId INVALID_ID; - - uint64_t bufferId; - uint64_t framenumber; - ReleaseCallbackId() {} - ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber) - : bufferId(bufferId), framenumber(framenumber) {} - status_t writeToParcel(Parcel* output) const override; - status_t readFromParcel(const Parcel* input) override; - - bool operator==(const ReleaseCallbackId& rhs) const { - return bufferId == rhs.bufferId && framenumber == rhs.framenumber; - } - bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); } - std::string to_string() const { - if (*this == INVALID_ID) return "INVALID_ID"; - - return "bufferId:" + std::to_string(bufferId) + - " framenumber:" + std::to_string(framenumber); - } -}; - struct ReleaseBufferCallbackIdHash { std::size_t operator()(const ReleaseCallbackId& key) const { return std::hash<uint64_t>()(key.bufferId); @@ -132,7 +107,7 @@ public: SurfaceStats() = default; SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence, - const sp<Fence>& prevReleaseFence, uint32_t hint, + const sp<Fence>& prevReleaseFence, std::optional<uint32_t> hint, uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats, std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId) : surfaceControl(sc), @@ -147,7 +122,7 @@ public: sp<IBinder> surfaceControl; std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1; sp<Fence> previousReleaseFence; - uint32_t transformHint = 0; + std::optional<uint32_t> transformHint = 0; uint32_t currentMaxAcquiredBufferCount = 0; FrameEventHistoryStats eventStats; std::vector<JankData> jankData; @@ -186,26 +161,6 @@ public: std::vector<TransactionStats> transactionStats; }; -class ITransactionCompletedListener : public IInterface { -public: - DECLARE_META_INTERFACE(TransactionCompletedListener) - - virtual void onTransactionCompleted(ListenerStats stats) = 0; - - virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence, - uint32_t currentMaxAcquiredBufferCount) = 0; - virtual void onTransactionQueueStalled() = 0; -}; - -class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> { -public: - BnTransactionCompletedListener() - : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; - class ListenerCallbacks { public: ListenerCallbacks(const sp<IBinder>& listener, @@ -267,4 +222,4 @@ struct ListenerCallbacksHash { } }; -} // namespace android +} // namespace android::gui diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h new file mode 100644 index 0000000000..142ee5a727 --- /dev/null +++ b/libs/gui/include/gui/ReleaseCallbackId.h @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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/Parcel.h> +#include <binder/Parcelable.h> + +#include <cstdint> + +namespace android::gui { + +class ReleaseCallbackId : public Parcelable { +public: + static const ReleaseCallbackId INVALID_ID; + + uint64_t bufferId; + uint64_t framenumber; + ReleaseCallbackId() {} + ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber) + : bufferId(bufferId), framenumber(framenumber) {} + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + bool operator==(const ReleaseCallbackId& rhs) const { + return bufferId == rhs.bufferId && framenumber == rhs.framenumber; + } + bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); } + std::string to_string() const { + if (*this == INVALID_ID) return "INVALID_ID"; + + return "bufferId:" + std::to_string(bufferId) + + " framenumber:" + std::to_string(framenumber); + } +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h index 724c11c881..6e17791a29 100644 --- a/libs/gui/include/gui/ScreenCaptureResults.h +++ b/libs/gui/include/gui/ScreenCaptureResults.h @@ -19,6 +19,7 @@ #include <binder/Parcel.h> #include <binder/Parcelable.h> #include <ui/Fence.h> +#include <ui/FenceResult.h> #include <ui/GraphicBuffer.h> namespace android::gui { @@ -31,11 +32,10 @@ public: status_t readFromParcel(const android::Parcel* parcel) override; sp<GraphicBuffer> buffer; - sp<Fence> fence = Fence::NO_FENCE; + FenceResult fenceResult = Fence::NO_FENCE; bool capturedSecureLayers{false}; bool capturedHdrLayers{false}; ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; - status_t result = OK; }; } // namespace android::gui diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 862a4adf2e..b9ccdc9124 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -17,8 +17,8 @@ #ifndef ANDROID_GUI_SURFACE_H #define ANDROID_GUI_SURFACE_H +#include <android/gui/FrameTimelineInfo.h> #include <gui/BufferQueueDefs.h> -#include <gui/FrameTimelineInfo.h> #include <gui/HdrMetadata.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> @@ -41,6 +41,8 @@ class ISurfaceComposer; class ISurfaceComposer; +using gui::FrameTimelineInfo; + /* This is the same as ProducerListener except that onBuffersDiscarded is * called with a vector of graphic buffers instead of buffer slots. */ @@ -111,6 +113,24 @@ public: return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } + static sp<IGraphicBufferProducer> getIGraphicBufferProducer(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mGraphicBufferProducer; + } + return nullptr; + } + + static sp<IBinder> getSurfaceControlHandle(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mSurfaceControlHandle; + } + return nullptr; + } + /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. * * A sideband stream is a device-specific mechanism for passing buffers @@ -185,8 +205,8 @@ public: nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime); - status_t getWideColorSupport(bool* supported); - status_t getHdrSupport(bool* supported); + status_t getWideColorSupport(bool* supported) __attribute__((__deprecated__)); + status_t getHdrSupport(bool* supported) __attribute__((__deprecated__)); status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; @@ -283,6 +303,10 @@ private: int dispatchGetLastQueuedBuffer2(va_list args); int dispatchSetFrameTimelineInfo(va_list args); + std::mutex mNameMutex; + std::string mName; + const char* getDebugName(); + protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 9033e17c53..96d3a23bec 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -38,11 +38,17 @@ #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <ui/Rotation.h> +#include <ui/StaticDisplayInfo.h> + +#include <android/gui/ISurfaceComposerClient.h> + +#include <android/gui/BnTransactionCompletedListener.h> #include <gui/CpuConsumer.h> #include <gui/ISurfaceComposer.h> -#include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> +#include <gui/ListenerStats.h> +#include <gui/ReleaseCallbackId.h> #include <gui/SurfaceControl.h> #include <gui/WindowInfosListenerReporter.h> #include <math/vec3.h> @@ -52,20 +58,31 @@ namespace android { class HdrCapabilities; -class ISurfaceComposerClient; class IGraphicBufferProducer; class ITunnelModeEnabledListener; class Region; +using gui::BnTransactionCompletedListener; +using gui::CallbackId; +using gui::CallbackIdHash; using gui::DisplayCaptureArgs; +using gui::FrameEventHistoryStats; using gui::IRegionSamplingListener; +using gui::ISurfaceComposerClient; +using gui::ITransactionCompletedListener; +using gui::JankData; using gui::LayerCaptureArgs; +using gui::LayerMetadata; +using gui::ListenerStats; +using gui::ReleaseBufferCallbackIdHash; +using gui::ReleaseCallbackId; +using gui::SurfaceStats; struct SurfaceControlStats { SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, - uint32_t hint, FrameEventHistoryStats eventStats, + std::optional<uint32_t> hint, FrameEventHistoryStats eventStats, uint32_t currentMaxAcquiredBufferCount) : surfaceControl(sc), latchTime(latchTime), @@ -81,7 +98,7 @@ struct SurfaceControlStats { std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1; sp<Fence> presentFence; sp<Fence> previousReleaseFence; - uint32_t transformHint = 0; + std::optional<uint32_t> transformHint = 0; FrameEventHistoryStats frameEventStats; uint32_t currentMaxAcquiredBufferCount = 0; }; @@ -145,28 +162,21 @@ public: static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*); // Get immutable information about given physical display. - static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*); + static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*); - // Get dynamic information about given physical display. - static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*); + // Get dynamic information about given physical display from display id + static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*); // Shorthand for the active display mode from getDynamicDisplayInfo(). // TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function. static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*); // Sets the refresh rate boundaries for the display. - static status_t setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, float appRequestRefreshRateMax); + static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs&); // Gets the refresh rate boundaries for the display. static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax); + gui::DisplayModeSpecs*); // Get the coordinates of the display's native color primaries static status_t getDisplayNativePrimaries(const sp<IBinder>& display, @@ -178,6 +188,10 @@ public: // Gets if boot display mode operations are supported on a device static status_t getBootDisplayModeSupport(bool* support); + + // Gets the overlay properties of the device + static status_t getOverlaySupport(gui::OverlayProperties* outProperties); + // Sets the user-preferred display mode that a device should boot in static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId); // Clears the user-preferred display mode @@ -218,7 +232,7 @@ public: /** * Gets the context priority of surface flinger's render engine. */ - static int getGPUContextPriority(); + static int getGpuContextPriority(); /** * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is @@ -314,7 +328,7 @@ public: uint32_t w, // width in pixel uint32_t h, // height in pixel PixelFormat format, // pixel-format desired - uint32_t flags = 0, // usage flags + int32_t flags = 0, // usage flags const sp<IBinder>& parentHandle = nullptr, // parentHandle LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); @@ -324,21 +338,11 @@ public: uint32_t h, // height in pixel PixelFormat format, // pixel-format desired sp<SurfaceControl>* outSurface, - uint32_t flags = 0, // usage flags + int32_t flags = 0, // usage flags const sp<IBinder>& parentHandle = nullptr, // parentHandle LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); - //! Create a surface - sp<SurfaceControl> createWithSurfaceParent(const String8& name, // name of the surface - uint32_t w, // width in pixel - uint32_t h, // height in pixel - PixelFormat format, // pixel-format desired - uint32_t flags = 0, // usage flags - Surface* parent = nullptr, // parent - LayerMetadata metadata = LayerMetadata(), // metadata - uint32_t* outTransformHint = nullptr); - // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl // which is a parent of the root of the mirrored hierarchy. // @@ -350,6 +354,8 @@ public: // B B' sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface); + sp<SurfaceControl> mirrorDisplay(DisplayId displayId); + //! Create a virtual display static sp<IBinder> createDisplay(const String8& displayName, bool secure); @@ -358,16 +364,9 @@ public: //! Get stable IDs for connected physical displays static std::vector<PhysicalDisplayId> getPhysicalDisplayIds(); - static status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*); - static std::optional<PhysicalDisplayId> getInternalDisplayId(); //! Get token for a physical display given its stable ID static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId); - static sp<IBinder> getInternalDisplayToken(); - - static status_t enableVSyncInjections(bool enable); - - static status_t injectVSync(nsecs_t when); struct SCHash { std::size_t operator()(const sp<SurfaceControl>& sc) const { @@ -398,7 +397,10 @@ public: class Transaction : public Parcelable { private: + static sp<IBinder> sApplyToken; void releaseBufferIfOverwriting(const layer_state_t& state); + static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other); + static void clearFrameTimelineInfo(FrameTimelineInfo& t); protected: std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; @@ -408,14 +410,15 @@ public: uint64_t mId; - uint32_t mForceSynchronous = 0; uint32_t mTransactionNestCount = 0; bool mAnimation = false; bool mEarlyWakeupStart = false; bool mEarlyWakeupEnd = false; - // Indicates that the Transaction contains a buffer that should be cached - bool mContainsBuffer = false; + // Indicates that the Transaction may contain buffers that should be cached. The reason this + // is only a guess is that buffers can be removed before cache is called. This is only a + // hint that at some point a buffer was added to this transaction before apply was called. + bool mMayContainBuffer = false; // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction // to be presented. When it is not possible to present at exactly that time, it will be @@ -474,10 +477,9 @@ public: Transaction& merge(Transaction&& other); Transaction& show(const sp<SurfaceControl>& sc); Transaction& hide(const sp<SurfaceControl>& sc); - Transaction& setPosition(const sp<SurfaceControl>& sc, - float x, float y); - Transaction& setSize(const sp<SurfaceControl>& sc, - uint32_t w, uint32_t h); + Transaction& setPosition(const sp<SurfaceControl>& sc, float x, float y); + // b/243180033 remove once functions are not called from vendor code + Transaction& setSize(const sp<SurfaceControl>&, uint32_t, uint32_t) { return *this; } Transaction& setLayer(const sp<SurfaceControl>& sc, int32_t z); @@ -577,7 +579,9 @@ public: Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info); Transaction& setFocusedWindow(const gui::FocusRequest& request); - Transaction& syncInputWindows(); + + Transaction& addWindowInfosReportedListener( + sp<gui::IWindowInfosReportedListener> windowInfosReportedListener); // Set a color transform matrix on the given layer on the built-in display. Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, @@ -590,6 +594,9 @@ public: Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy); + Transaction& setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc, + int8_t compatibility); + // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that // the graphic producers should receive a transform hint as if the @@ -636,6 +643,9 @@ public: const Rect& destinationFrame); Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode); + Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable, float width, + const half4& color); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -667,6 +677,9 @@ public: * TODO (b/213644870): Remove all permissioned things from Transaction */ void sanitize(); + + static sp<IBinder> getDefaultApplyToken(); + static void setDefaultApplyToken(sp<IBinder> applyToken); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; @@ -714,6 +727,12 @@ protected: ReleaseCallbackThread mReleaseCallbackThread; private: + // Get dynamic information about given physical display from token + static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display, + ui::DynamicDisplayInfo*); + + static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo, + ui::DynamicDisplayInfo*& outInfo); virtual void onFirstRef(); mutable Mutex mLock; @@ -779,7 +798,7 @@ protected: // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for // std::recursive_mutex std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners; - std::unordered_map<void*, std::function<void()>> mQueueStallListeners; + std::unordered_map<void*, std::function<void(const std::string&)>> mQueueStallListeners; public: static sp<TransactionCompletedListener> getInstance(); @@ -797,7 +816,7 @@ public: const sp<SurfaceControl>& surfaceControl, const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds); - void addQueueStallListener(std::function<void()> stallListener, void* id); + void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id); void removeQueueStallListener(void *id); /* @@ -819,17 +838,17 @@ public: void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback); // BnTransactionCompletedListener overrides - void onTransactionCompleted(ListenerStats stats) override; - void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, - uint32_t currentMaxAcquiredBufferCount) override; + binder::Status onTransactionCompleted(const ListenerStats& stats) override; + binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId, + const std::optional<os::ParcelFileDescriptor>& releaseFenceFd, + int32_t currentMaxAcquiredBufferCount) override; + binder::Status onTransactionQueueStalled(const std::string& reason) override; void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId); // For Testing Only static void setInstance(const sp<TransactionCompletedListener>&); - void onTransactionQueueStalled() override; - private: ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&); static sp<TransactionCompletedListener> sInstance; diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index b72cf8390e..1d4fc7f06d 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -24,11 +24,12 @@ #include <utils/RefBase.h> #include <utils/threads.h> +#include <android/gui/ISurfaceComposerClient.h> + #include <ui/FrameStats.h> #include <ui/PixelFormat.h> #include <ui/Region.h> -#include <gui/ISurfaceComposerClient.h> #include <math/vec3.h> namespace android { @@ -77,6 +78,7 @@ public: sp<IBinder> getHandle() const; sp<IBinder> getLayerStateHandle() const; int32_t getLayerId() const; + const std::string& getName() const; sp<IGraphicBufferProducer> getIGraphicBufferProducer(); @@ -93,9 +95,9 @@ public: explicit SurfaceControl(const sp<SurfaceControl>& other); SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, int32_t layerId, - uint32_t width = 0, uint32_t height = 0, PixelFormat format = 0, - uint32_t transformHint = 0, uint32_t flags = 0); + int32_t layerId, const std::string& layerName, uint32_t width = 0, + uint32_t height = 0, PixelFormat format = 0, uint32_t transformHint = 0, + uint32_t flags = 0); sp<SurfaceControl> getParentingLayer(); @@ -115,13 +117,13 @@ private: status_t validate() const; sp<SurfaceComposerClient> mClient; - sp<IBinder> mHandle; - sp<IGraphicBufferProducer> mGraphicBufferProducer; + sp<IBinder> mHandle; mutable Mutex mLock; mutable sp<Surface> mSurfaceData; mutable sp<BLASTBufferQueue> mBbq; mutable sp<SurfaceControl> mBbqChild; int32_t mLayerId = 0; + std::string mName; uint32_t mTransformHint = 0; uint32_t mWidth = 0; uint32_t mHeight = 0; diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h index 0784fbc058..bcf565a494 100644 --- a/libs/gui/include/gui/SyncScreenCaptureListener.h +++ b/libs/gui/include/gui/SyncScreenCaptureListener.h @@ -34,7 +34,9 @@ public: ScreenCaptureResults waitForResults() { std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future(); const auto screenCaptureResults = resultsFuture.get(); - screenCaptureResults.fence->waitForever(""); + if (screenCaptureResults.fenceResult.ok()) { + screenCaptureResults.fenceResult.value()->waitForever(""); + } return screenCaptureResults; } @@ -42,4 +44,4 @@ private: std::promise<ScreenCaptureResults> resultsPromise; }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h index e5d268445c..441b833b5d 100644 --- a/libs/gui/include/gui/TraceUtils.h +++ b/libs/gui/include/gui/TraceUtils.h @@ -21,11 +21,20 @@ #include <cutils/trace.h> #include <utils/Trace.h> -#define ATRACE_FORMAT(fmt, ...) \ - TraceUtils::TraceEnder __traceEnder = \ - (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder()) +#define ATRACE_FORMAT(fmt, ...) \ + TraceUtils::TraceEnder traceEnder = \ + (CC_UNLIKELY(ATRACE_ENABLED()) && \ + (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), true), \ + TraceUtils::TraceEnder()) -#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__) +#define ATRACE_FORMAT_INSTANT(fmt, ...) \ + (CC_UNLIKELY(ATRACE_ENABLED()) && (TraceUtils::instantFormat(fmt, ##__VA_ARGS__), true)) + +#define ALOGE_AND_TRACE(fmt, ...) \ + do { \ + ALOGE(fmt, ##__VA_ARGS__); \ + ATRACE_FORMAT_INSTANT(fmt, ##__VA_ARGS__); \ + } while (false) namespace android { @@ -37,8 +46,6 @@ public: }; static void atraceFormatBegin(const char* fmt, ...) { - if (CC_LIKELY(!ATRACE_ENABLED())) return; - const int BUFFER_SIZE = 256; va_list ap; char buf[BUFFER_SIZE]; @@ -50,6 +57,17 @@ public: ATRACE_BEGIN(buf); } -}; // class TraceUtils + static void instantFormat(const char* fmt, ...) { + const int BUFFER_SIZE = 256; + va_list ap; + char buf[BUFFER_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUFFER_SIZE, fmt, ap); + va_end(ap); + + ATRACE_INSTANT(buf); + } +}; -} /* namespace android */ +} // namespace android diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h index 8e99539fe9..dfdae214d2 100644 --- a/libs/gui/include/gui/VsyncEventData.h +++ b/libs/gui/include/gui/VsyncEventData.h @@ -16,7 +16,7 @@ #pragma once -#include <gui/FrameTimelineInfo.h> +#include <android/gui/FrameTimelineInfo.h> #include <array> diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 169f7f022b..b01a3db52d 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -171,6 +171,8 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::SPY), INTERCEPTS_STYLUS = static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), + CLONE = + static_cast<uint32_t>(os::InputConfig::CLONE), // clang-format on }; @@ -236,8 +238,6 @@ struct WindowInfo : public Parcelable { void setInputConfig(ftl::Flags<InputConfig> config, bool value); - bool isClone = false; - void addTouchableRegion(const Rect& region); bool touchableRegionContainsPoint(int32_t x, int32_t y) const; @@ -272,6 +272,7 @@ public: WindowInfoHandle(const WindowInfo& other); inline const WindowInfo* getInfo() const { return &mInfo; } + inline WindowInfo* editInfo() { return &mInfo; } sp<IBinder> getToken() const; diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h index 3b4aed442e..2754442a95 100644 --- a/libs/gui/include/gui/WindowInfosListenerReporter.h +++ b/libs/gui/include/gui/WindowInfosListenerReporter.h @@ -17,15 +17,14 @@ #pragma once #include <android/gui/BnWindowInfosListener.h> +#include <android/gui/ISurfaceComposer.h> #include <android/gui/IWindowInfosReportedListener.h> #include <binder/IBinder.h> -#include <gui/ISurfaceComposer.h> #include <gui/SpHash.h> #include <gui/WindowInfosListener.h> #include <unordered_set> namespace android { -class ISurfaceComposer; class WindowInfosListenerReporter : public gui::BnWindowInfosListener { public: @@ -33,17 +32,17 @@ public: binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&, const std::vector<gui::DisplayInfo>&, const sp<gui::IWindowInfosReportedListener>&) override; - status_t addWindowInfosListener( - const sp<gui::WindowInfosListener>& windowInfosListener, const sp<ISurfaceComposer>&, + const sp<gui::WindowInfosListener>& windowInfosListener, + const sp<gui::ISurfaceComposer>&, std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo); status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener, - const sp<ISurfaceComposer>& surfaceComposer); - void reconnect(const sp<ISurfaceComposer>&); + const sp<gui::ISurfaceComposer>& surfaceComposer); + void reconnect(const sp<gui::ISurfaceComposer>&); private: std::mutex mListenersMutex; - std::unordered_set<sp<gui::WindowInfosListener>, SpHash<gui::WindowInfosListener>> + std::unordered_set<sp<gui::WindowInfosListener>, gui::SpHash<gui::WindowInfosListener>> mWindowInfosListeners GUARDED_BY(mListenersMutex); std::vector<gui::WindowInfo> mLastWindowInfos GUARDED_BY(mListenersMutex); diff --git a/libs/gui/include/gui/fake/BufferData.h b/libs/gui/include/gui/fake/BufferData.h new file mode 100644 index 0000000000..725d11c313 --- /dev/null +++ b/libs/gui/include/gui/fake/BufferData.h @@ -0,0 +1,51 @@ +/* + * Copyright 2022 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 <gui/LayerState.h> + +namespace android::fake { + +// Class which exposes buffer properties from BufferData without holding on to an actual buffer +class BufferData : public android::BufferData { +public: + BufferData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat, + uint64_t outUsage) + : mBufferId(bufferId), + mWidth(width), + mHeight(height), + mPixelFormat(pixelFormat), + mOutUsage(outUsage) {} + bool hasBuffer() const override { return mBufferId != 0; } + bool hasSameBuffer(const android::BufferData& other) const override { + return getId() == other.getId() && frameNumber == other.frameNumber; + } + uint32_t getWidth() const override { return mWidth; } + uint32_t getHeight() const override { return mHeight; } + uint64_t getId() const override { return mBufferId; } + PixelFormat getPixelFormat() const override { return mPixelFormat; } + uint64_t getUsage() const override { return mOutUsage; } + +private: + uint64_t mBufferId; + uint32_t mWidth; + uint32_t mHeight; + int32_t mPixelFormat; + uint64_t mOutUsage; +}; + +} // namespace android::fake diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h index 9a96976c0f..6352a5851c 100644 --- a/libs/gui/include/private/gui/ComposerServiceAIDL.h +++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h @@ -20,6 +20,7 @@ #include <sys/types.h> #include <android/gui/ISurfaceComposer.h> +#include <ui/DisplayId.h> #include <utils/Singleton.h> #include <utils/StrongPointer.h> @@ -50,28 +51,6 @@ public: // Get a connection to the Composer Service. This will block until // a connection is established. Returns null if permission is denied. static sp<gui::ISurfaceComposer> getComposerService(); - - // the following two methods are moved from ISurfaceComposer.h - // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic. - std::optional<PhysicalDisplayId> getInternalDisplayId() const { - std::vector<int64_t> displayIds; - binder::Status status = mComposerService->getPhysicalDisplayIds(&displayIds); - return (!status.isOk() || displayIds.empty()) - ? std::nullopt - : DisplayId::fromValue<PhysicalDisplayId>( - static_cast<uint64_t>(displayIds.front())); - } - - // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic. - sp<IBinder> getInternalDisplayToken() const { - const auto displayId = getInternalDisplayId(); - if (!displayId) return nullptr; - sp<IBinder> display; - binder::Status status = - mComposerService->getPhysicalDisplayToken(static_cast<int64_t>(displayId->value), - &display); - return status.isOk() ? display : nullptr; - } }; // --------------------------------------------------------------------------- diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index fa54c7d1f6..183acc12cc 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -24,6 +24,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", "DisplayInfo_test.cpp", diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index b993289e6a..cf2593dc81 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -19,6 +19,7 @@ #include <gui/BLASTBufferQueue.h> #include <android/hardware/graphics/common/1.2/types.h> +#include <gui/AidlStatusUtil.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> #include <gui/FrameTimestamps.h> @@ -187,7 +188,10 @@ protected: void SetUp() { mComposer = ComposerService::getComposerService(); mClient = new SurfaceComposerClient(); - mDisplayToken = mClient->getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + // display 0 is picked as this test is not much display depedent + mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_NE(nullptr, mDisplayToken.get()); Transaction t; t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK); @@ -305,11 +309,12 @@ protected: const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); binder::Status status = sf->captureDisplay(captureArgs, captureListener); - if (status.transactionError() != NO_ERROR) { - return status.transactionError(); + status_t err = gui::aidl_utils::statusTFromBinderStatus(status); + if (err != NO_ERROR) { + return err; } captureResults = captureListener->waitForResults(); - return captureResults.result; + return fenceStatus(captureResults.fenceResult); } void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b, @@ -1146,6 +1151,7 @@ TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) { ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + sync.apply(); } // This test will currently fail because the old surfacecontrol will steal the last presented buffer diff --git a/libs/gui/tests/CompositorTiming_test.cpp b/libs/gui/tests/CompositorTiming_test.cpp new file mode 100644 index 0000000000..d8bb21d582 --- /dev/null +++ b/libs/gui/tests/CompositorTiming_test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 <gui/CompositorTiming.h> + +namespace android::test { +namespace { + +constexpr nsecs_t kMillisecond = 1'000'000; +constexpr nsecs_t kVsyncPeriod = 8'333'333; +constexpr nsecs_t kVsyncPhase = -2'166'667; +constexpr nsecs_t kIdealLatency = -kVsyncPhase; + +} // namespace + +TEST(CompositorTimingTest, InvalidVsyncPeriod) { + const nsecs_t vsyncDeadline = systemTime(); + constexpr nsecs_t kInvalidVsyncPeriod = -1; + + const gui::CompositorTiming timing(vsyncDeadline, kInvalidVsyncPeriod, kVsyncPhase, + kIdealLatency); + + EXPECT_EQ(timing.deadline, 0); + EXPECT_EQ(timing.interval, gui::CompositorTiming::kDefaultVsyncPeriod); + EXPECT_EQ(timing.presentLatency, gui::CompositorTiming::kDefaultVsyncPeriod); +} + +TEST(CompositorTimingTest, PresentLatencySnapping) { + for (nsecs_t presentDelay = 0, compositeTime = systemTime(); presentDelay < 10 * kVsyncPeriod; + presentDelay += kMillisecond, compositeTime += kVsyncPeriod) { + const nsecs_t presentLatency = kIdealLatency + presentDelay; + const nsecs_t vsyncDeadline = compositeTime + presentLatency + kVsyncPeriod; + + const gui::CompositorTiming timing(vsyncDeadline, kVsyncPeriod, kVsyncPhase, + presentLatency); + + EXPECT_EQ(timing.deadline, compositeTime + presentDelay + kVsyncPeriod); + EXPECT_EQ(timing.interval, kVsyncPeriod); + + // The presentDelay should be rounded to a multiple of the VSYNC period, such that the + // remainder (presentLatency % interval) always evaluates to the VSYNC phase offset. + EXPECT_GE(timing.presentLatency, kIdealLatency); + EXPECT_EQ(timing.presentLatency % timing.interval, kIdealLatency); + } +} + +} // namespace android::test diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp index b647aaba8f..0a2750a4dd 100644 --- a/libs/gui/tests/DisplayedContentSampling_test.cpp +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp @@ -32,7 +32,10 @@ protected: void SetUp() { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(OK, mComposerClient->initCheck()); - mDisplayToken = mComposerClient->getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + // display 0 is picked for now, can extend to support all displays if needed + mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_TRUE(mDisplayToken); } diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 2637f59b5e..3344e0b690 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -360,8 +360,10 @@ public: void SetUp() { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - const auto display = mComposerClient->getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + // display 0 is picked for now, can extend to support all displays if needed + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_NE(display, nullptr); ui::DisplayMode mode; diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index c9106bed4c..b18b544257 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -19,14 +19,16 @@ #include <android/gui/BnRegionSamplingListener.h> #include <binder/ProcessState.h> +#include <gui/AidlStatusUtil.h> #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <utils/Looper.h> using namespace std::chrono_literals; +using android::gui::aidl_utils::statusTFromBinderStatus; namespace android::test { @@ -242,24 +244,33 @@ protected: }; TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) { - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); - const Rect sampleArea{100, 100, 200, 200}; + gui::ARect sampleArea; + sampleArea.left = 100; + sampleArea.top = 100; + sampleArea.right = 200; + sampleArea.bottom = 200; // Passing in composer service as the layer handle should not crash, we'll // treat it as a layer that no longer exists and silently allow sampling to // occur. - status_t status = composer->addRegionSamplingListener(sampleArea, - IInterface::asBinder(composer), listener); - ASSERT_EQ(NO_ERROR, status); + binder::Status status = + composer->addRegionSamplingListener(sampleArea, IInterface::asBinder(composer), + listener); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); composer->removeRegionSamplingListener(listener); } TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { fill_render(rgba_green); - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); - const Rect sampleArea{100, 100, 200, 200}; + gui::ARect sampleArea; + sampleArea.left = 100; + sampleArea.top = 100; + sampleArea.right = 200; + sampleArea.bottom = 200; composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; @@ -271,9 +282,13 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { fill_render(rgba_green); - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); - const Rect sampleArea{100, 100, 200, 200}; + gui::ARect sampleArea; + sampleArea.left = 100; + sampleArea.top = 100; + sampleArea.right = 200; + sampleArea.bottom = 200; composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; @@ -291,13 +306,21 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { fill_render(rgba_green); - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> greenListener = new Listener(); - const Rect greenSampleArea{100, 100, 200, 200}; + gui::ARect greenSampleArea; + greenSampleArea.left = 100; + greenSampleArea.top = 100; + greenSampleArea.right = 200; + greenSampleArea.bottom = 200; composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener); sp<Listener> grayListener = new Listener(); - const Rect graySampleArea{500, 100, 600, 200}; + gui::ARect graySampleArea; + graySampleArea.left = 500; + graySampleArea.top = 100; + graySampleArea.right = 600; + graySampleArea.bottom = 200; composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener); EXPECT_TRUE(grayListener->wait_event(300ms)) @@ -312,29 +335,49 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { } TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) { - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); - const Rect sampleArea{100, 100, 200, 200}; + + gui::ARect invalidRect; + invalidRect.left = Rect::INVALID_RECT.left; + invalidRect.top = Rect::INVALID_RECT.top; + invalidRect.right = Rect::INVALID_RECT.right; + invalidRect.bottom = Rect::INVALID_RECT.bottom; + + gui::ARect sampleArea; + sampleArea.left = 100; + sampleArea.top = 100; + sampleArea.right = 200; + sampleArea.bottom = 200; // Invalid input sampleArea EXPECT_EQ(BAD_VALUE, - composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(), - listener)); + statusTFromBinderStatus(composer->addRegionSamplingListener(invalidRect, + mTopLayer->getHandle(), + listener))); listener->reset(); // Invalid input binder - EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener)); + EXPECT_EQ(NO_ERROR, + statusTFromBinderStatus( + composer->addRegionSamplingListener(sampleArea, NULL, listener))); // Invalid input listener EXPECT_EQ(BAD_VALUE, - composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL)); - EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL)); + statusTFromBinderStatus(composer->addRegionSamplingListener(sampleArea, + mTopLayer->getHandle(), + NULL))); + EXPECT_EQ(BAD_VALUE, statusTFromBinderStatus(composer->removeRegionSamplingListener(NULL))); // remove the listener composer->removeRegionSamplingListener(listener); } TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) { fill_render(rgba_green); - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); - const Rect sampleArea{100, 100, 200, 200}; + gui::ARect sampleArea; + sampleArea.left = 100; + sampleArea.top = 100; + sampleArea.right = 200; + sampleArea.bottom = 200; composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); fill_render(rgba_green); @@ -349,13 +392,18 @@ TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) { } TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) { - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); sp<Listener> listener = new Listener(); Rect sampleArea{100, 100, 200, 200}; + gui::ARect sampleAreaA; + sampleAreaA.left = sampleArea.left; + sampleAreaA.top = sampleArea.top; + sampleAreaA.right = sampleArea.right; + sampleAreaA.bottom = sampleArea.bottom; // Test: listener in (100, 100). See layer before move, no layer after move. fill_render(rgba_blue); - composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener); EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; EXPECT_NEAR(listener->luma(), luma_blue, error_margin); listener->reset(); @@ -367,7 +415,11 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) { // Test: listener offset to (600, 600). No layer before move, see layer after move. fill_render(rgba_green); sampleArea.offsetTo(600, 600); - composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + sampleAreaA.left = sampleArea.left; + sampleAreaA.top = sampleArea.top; + sampleAreaA.right = sampleArea.right; + sampleAreaA.bottom = sampleArea.bottom; + composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener); EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; EXPECT_NEAR(listener->luma(), luma_gray, error_margin); listener->reset(); diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp index a083a228a6..f98437b4f8 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -26,7 +26,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <utils/Trace.h> using namespace std::chrono_literals; @@ -121,10 +121,22 @@ int main(int, const char**) { 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(), + gui::ARect homeButtonAreaA; + homeButtonAreaA.left = 490; + homeButtonAreaA.top = 1606; + homeButtonAreaA.right = 590; + homeButtonAreaA.bottom = 1654; + + gui::ARect backButtonAreaA; + backButtonAreaA.left = 200; + backButtonAreaA.top = 1606; + backButtonAreaA.right = 248; + backButtonAreaA.bottom = 1654; + + sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService(); + composer->addRegionSamplingListener(homeButtonAreaA, homeButton->getStopLayerHandle(), homeButton); - composer->addRegionSamplingListener(backButtonArea, backButton->getStopLayerHandle(), + composer->addRegionSamplingListener(backButtonAreaA, backButton->getStopLayerHandle(), backButton); ProcessState::self()->startThreadPool(); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 065cd7a5c7..55242dfd39 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -24,6 +24,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <binder/ProcessState.h> #include <configstore/Utils.h> +#include <gui/AidlStatusUtil.h> #include <gui/BufferItemConsumer.h> #include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> @@ -212,11 +213,12 @@ protected: const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); binder::Status status = sf->captureDisplay(captureArgs, captureListener); - if (status.transactionError() != NO_ERROR) { - return status.transactionError(); + status_t err = gui::aidl_utils::statusTFromBinderStatus(status); + if (err != NO_ERROR) { + return err; } captureResults = captureListener->waitForResults(); - return captureResults.result; + return fenceStatus(captureResults.fenceResult); } sp<Surface> mSurface; @@ -261,7 +263,10 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { sp<ANativeWindow> anw(mSurface); // Verify the screenshot works with no protected buffers. - const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + // display 0 is picked for now, can extend to support all displays if needed + const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); DisplayCaptureArgs captureArgs; @@ -690,13 +695,8 @@ public: mSupportsPresent = supportsPresent; } - sp<ISurfaceComposerClient> createConnection() override { return nullptr; } - sp<IDisplayEventConnection> createDisplayEventConnection( - ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override { - return nullptr; - } status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/, - const Vector<ComposerState>& /*state*/, + Vector<ComposerState>& /*state*/, const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, const sp<IBinder>& /*applyToken*/, const InputWindowCommands& /*inputWindowCommands*/, @@ -708,260 +708,229 @@ public: return NO_ERROR; } - void bootFinished() override {} - bool authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& /*surface*/) const override { - return false; - } +protected: + IBinder* onAsBinder() override { return nullptr; } - status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) - const override { - *outSupported = { - FrameEvent::REQUESTED_PRESENT, - FrameEvent::ACQUIRE, - FrameEvent::LATCH, - FrameEvent::FIRST_REFRESH_START, - FrameEvent::LAST_REFRESH_START, - FrameEvent::GPU_COMPOSITION_DONE, - FrameEvent::DEQUEUE_READY, - FrameEvent::RELEASE - }; - if (mSupportsPresent) { - outSupported->push_back( - FrameEvent::DISPLAY_PRESENT); - } - return NO_ERROR; - } +private: + bool mSupportsPresent{true}; +}; - status_t getStaticDisplayInfo(const sp<IBinder>& /*display*/, ui::StaticDisplayInfo*) override { - return NO_ERROR; - } - status_t getDynamicDisplayInfo(const sp<IBinder>& /*display*/, - ui::DynamicDisplayInfo*) override { - return NO_ERROR; - } - status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/, - ui::DisplayPrimaries& /*primaries*/) override { - return NO_ERROR; - } - status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { - return NO_ERROR; - } - status_t setBootDisplayMode(const sp<IBinder>& /*display*/, ui::DisplayModeId /*id*/) override { - return NO_ERROR; - } +class FakeSurfaceComposerAIDL : public gui::ISurfaceComposer { +public: + ~FakeSurfaceComposerAIDL() override {} - status_t clearAnimationFrameStats() override { return NO_ERROR; } - status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { - return NO_ERROR; - } - status_t overrideHdrTypes(const sp<IBinder>& /*display*/, - const std::vector<ui::Hdr>& /*hdrTypes*/) override { - return NO_ERROR; - } - status_t onPullAtom(const int32_t /*atomId*/, std::string* /*outData*/, - bool* /*success*/) override { - return NO_ERROR; - } - status_t enableVSyncInjections(bool /*enable*/) override { - return NO_ERROR; + void setSupportsPresent(bool supportsPresent) { mSupportsPresent = supportsPresent; } + + binder::Status bootFinished() override { return binder::Status::ok(); } + + binder::Status createDisplayEventConnection( + VsyncSource /*vsyncSource*/, EventRegistration /*eventRegistration*/, + sp<gui::IDisplayEventConnection>* outConnection) override { + *outConnection = nullptr; + return binder::Status::ok(); } - status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } - status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) override { - return NO_ERROR; + + binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override { + *outClient = nullptr; + return binder::Status::ok(); } - status_t getCompositionPreference( - ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/, - ui::Dataspace* /*outWideColorGamutDataspace*/, - ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override { - return NO_ERROR; + + binder::Status createDisplay(const std::string& /*displayName*/, bool /*secure*/, + sp<IBinder>* /*outDisplay*/) override { + return binder::Status::ok(); } - status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/, - ui::PixelFormat* /*outFormat*/, - ui::Dataspace* /*outDataspace*/, - uint8_t* /*outComponentMask*/) const override { - return NO_ERROR; + + binder::Status destroyDisplay(const sp<IBinder>& /*display*/) override { + return binder::Status::ok(); } - status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/, - uint8_t /*componentMask*/, - uint64_t /*maxFrames*/) override { - return NO_ERROR; + + binder::Status getPhysicalDisplayIds(std::vector<int64_t>* /*outDisplayIds*/) override { + return binder::Status::ok(); } - status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/, - uint64_t /*timestamp*/, - DisplayedFrameStats* /*outStats*/) const override { - return NO_ERROR; + + binder::Status getPhysicalDisplayToken(int64_t /*displayId*/, + sp<IBinder>* /*outDisplay*/) override { + return binder::Status::ok(); } - status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; } - status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; } + binder::Status setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override { + return binder::Status::ok(); + } - status_t addRegionSamplingListener(const Rect& /*samplingArea*/, - const sp<IBinder>& /*stopLayerHandle*/, - const sp<IRegionSamplingListener>& /*listener*/) override { - return NO_ERROR; + binder::Status getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) override { + *outSupported = {FrameEvent::REQUESTED_PRESENT, + FrameEvent::ACQUIRE, + FrameEvent::LATCH, + FrameEvent::FIRST_REFRESH_START, + FrameEvent::LAST_REFRESH_START, + FrameEvent::GPU_COMPOSITION_DONE, + FrameEvent::DEQUEUE_READY, + FrameEvent::RELEASE}; + if (mSupportsPresent) { + outSupported->push_back(FrameEvent::DISPLAY_PRESENT); + } + return binder::Status::ok(); } - status_t removeRegionSamplingListener( - const sp<IRegionSamplingListener>& /*listener*/) override { - return NO_ERROR; + + binder::Status getDisplayStats(const sp<IBinder>& /*display*/, + gui::DisplayStatInfo* /*outStatInfo*/) override { + return binder::Status::ok(); } - status_t addFpsListener(int32_t /*taskId*/, const sp<gui::IFpsListener>& /*listener*/) { - return NO_ERROR; + + binder::Status getDisplayState(const sp<IBinder>& /*display*/, + gui::DisplayState* /*outState*/) override { + return binder::Status::ok(); } - status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; } - status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& /*listener*/) { - return NO_ERROR; + binder::Status getStaticDisplayInfo(int64_t /*displayId*/, + gui::StaticDisplayInfo* /*outInfo*/) override { + return binder::Status::ok(); } - status_t removeTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& /*listener*/) { - return NO_ERROR; + binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/, + gui::DynamicDisplayInfo* /*outInfo*/) override { + return binder::Status::ok(); } - status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, - ui::DisplayModeId /*defaultMode*/, - bool /*allowGroupSwitching*/, - float /*primaryRefreshRateMin*/, - float /*primaryRefreshRateMax*/, - float /*appRequestRefreshRateMin*/, - float /*appRequestRefreshRateMax*/) { - return NO_ERROR; + binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/, + gui::DynamicDisplayInfo* /*outInfo*/) override { + return binder::Status::ok(); } - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, - ui::DisplayModeId* /*outDefaultMode*/, - bool* /*outAllowGroupSwitching*/, - float* /*outPrimaryRefreshRateMin*/, - float* /*outPrimaryRefreshRateMax*/, - float* /*outAppRequestRefreshRateMin*/, - float* /*outAppRequestRefreshRateMax*/) override { - return NO_ERROR; - }; - status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/, - float /*lightPosY*/, float /*lightPosZ*/, - float /*lightRadius*/) override { - return NO_ERROR; + binder::Status getDisplayNativePrimaries(const sp<IBinder>& /*display*/, + gui::DisplayPrimaries* /*outPrimaries*/) override { + return binder::Status::ok(); } - status_t getDisplayDecorationSupport( - const sp<IBinder>& /*displayToken*/, - std::optional<DisplayDecorationSupport>* /*outSupport*/) const override { - return NO_ERROR; + binder::Status setActiveColorMode(const sp<IBinder>& /*display*/, int /*colorMode*/) override { + return binder::Status::ok(); } - status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/, - int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override { - return NO_ERROR; + binder::Status setBootDisplayMode(const sp<IBinder>& /*display*/, + int /*displayModeId*/) override { + return binder::Status::ok(); } - status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/, - const FrameTimelineInfo& /*frameTimelineInfo*/) override { - return NO_ERROR; + binder::Status clearBootDisplayMode(const sp<IBinder>& /*display*/) override { + return binder::Status::ok(); } - status_t addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& /*listener*/) override { - return NO_ERROR; + binder::Status getBootDisplayModeSupport(bool* /*outMode*/) override { + return binder::Status::ok(); } - int getGPUContextPriority() override { return 0; }; + binder::Status setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override { + return binder::Status::ok(); + } - status_t getMaxAcquiredBufferCount(int* /*buffers*/) const override { return NO_ERROR; } + binder::Status setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override { + return binder::Status::ok(); + } - status_t addWindowInfosListener( - const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override { - return NO_ERROR; + binder::Status captureDisplay(const DisplayCaptureArgs&, + const sp<IScreenCaptureListener>&) override { + return binder::Status::ok(); } - status_t removeWindowInfosListener( - const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override { - return NO_ERROR; + binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override { + return binder::Status::ok(); } - status_t setOverrideFrameRate(uid_t /*uid*/, float /*frameRate*/) override { return NO_ERROR; } + binder::Status captureLayers(const LayerCaptureArgs&, + const sp<IScreenCaptureListener>&) override { + return binder::Status::ok(); + } -protected: - IBinder* onAsBinder() override { return nullptr; } + binder::Status clearAnimationFrameStats() override { return binder::Status::ok(); } -private: - bool mSupportsPresent{true}; -}; + binder::Status getAnimationFrameStats(gui::FrameStats* /*outStats*/) override { + return binder::Status::ok(); + } -class FakeSurfaceComposerAIDL : public gui::ISurfaceComposer { -public: - ~FakeSurfaceComposerAIDL() override {} + binder::Status overrideHdrTypes(const sp<IBinder>& /*display*/, + const std::vector<int32_t>& /*hdrTypes*/) override { + return binder::Status::ok(); + } - void setSupportsPresent(bool supportsPresent) { mSupportsPresent = supportsPresent; } + binder::Status onPullAtom(int32_t /*atomId*/, gui::PullAtomData* /*outPullData*/) override { + return binder::Status::ok(); + } - binder::Status createDisplay(const std::string& /*displayName*/, bool /*secure*/, - sp<IBinder>* /*outDisplay*/) override { + binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* /*outLayers*/) override { return binder::Status::ok(); } - binder::Status destroyDisplay(const sp<IBinder>& /*display*/) override { + binder::Status getColorManagement(bool* /*outGetColorManagement*/) override { return binder::Status::ok(); } - binder::Status getPhysicalDisplayIds(std::vector<int64_t>* /*outDisplayIds*/) override { + binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override { return binder::Status::ok(); } - binder::Status getPrimaryPhysicalDisplayId(int64_t* /*outDisplayId*/) override { + binder::Status getDisplayedContentSamplingAttributes( + const sp<IBinder>& /*display*/, gui::ContentSamplingAttributes* /*outAttrs*/) override { return binder::Status::ok(); } - binder::Status getPhysicalDisplayToken(int64_t /*displayId*/, - sp<IBinder>* /*outDisplay*/) override { + binder::Status setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/, + int8_t /*componentMask*/, + int64_t /*maxFrames*/) override { return binder::Status::ok(); } - binder::Status setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override { + binder::Status getProtectedContentSupport(bool* /*outSupporte*/) override { return binder::Status::ok(); } - binder::Status getDisplayStats(const sp<IBinder>& /*display*/, - gui::DisplayStatInfo* /*outStatInfo*/) override { + binder::Status getDisplayedContentSample(const sp<IBinder>& /*display*/, int64_t /*maxFrames*/, + int64_t /*timestamp*/, + gui::DisplayedFrameStats* /*outStats*/) override { return binder::Status::ok(); } - binder::Status getDisplayState(const sp<IBinder>& /*display*/, - gui::DisplayState* /*outState*/) override { + binder::Status isWideColorDisplay(const sp<IBinder>& /*token*/, + bool* /*outIsWideColorDisplay*/) override { return binder::Status::ok(); } - binder::Status clearBootDisplayMode(const sp<IBinder>& /*display*/) override { + binder::Status addRegionSamplingListener( + const gui::ARect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/, + const sp<gui::IRegionSamplingListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status getBootDisplayModeSupport(bool* /*outMode*/) override { + binder::Status removeRegionSamplingListener( + const sp<gui::IRegionSamplingListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override { + binder::Status addFpsListener(int32_t /*taskId*/, + const sp<gui::IFpsListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override { + binder::Status removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status captureDisplay(const DisplayCaptureArgs&, - const sp<IScreenCaptureListener>&) override { + binder::Status addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override { + binder::Status removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& /*listener*/) override { return binder::Status::ok(); } - binder::Status captureLayers(const LayerCaptureArgs&, - const sp<IScreenCaptureListener>&) override { + binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, + const gui::DisplayModeSpecs&) override { return binder::Status::ok(); } - binder::Status isWideColorDisplay(const sp<IBinder>& /*token*/, - bool* /*outIsWideColorDisplay*/) override { + binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, + gui::DisplayModeSpecs*) override { return binder::Status::ok(); } @@ -989,6 +958,44 @@ public: binder::Status notifyPowerBoost(int /*boostId*/) override { return binder::Status::ok(); } + binder::Status setGlobalShadowSettings(const gui::Color& /*ambientColor*/, + const gui::Color& /*spotColor*/, float /*lightPosY*/, + float /*lightPosZ*/, float /*lightRadius*/) override { + return binder::Status::ok(); + } + + binder::Status getDisplayDecorationSupport( + const sp<IBinder>& /*displayToken*/, + std::optional<gui::DisplayDecorationSupport>* /*outSupport*/) override { + return binder::Status::ok(); + } + + binder::Status setOverrideFrameRate(int32_t /*uid*/, float /*frameRate*/) override { + return binder::Status::ok(); + } + + binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override { + return binder::Status::ok(); + } + + binder::Status getMaxAcquiredBufferCount(int32_t* /*buffers*/) override { + return binder::Status::ok(); + } + + binder::Status addWindowInfosListener( + const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) override { + return binder::Status::ok(); + } + + binder::Status removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) override { + return binder::Status::ok(); + } + + binder::Status getOverlaySupport(gui::OverlayProperties* /*properties*/) override { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } @@ -1034,10 +1041,10 @@ protected: class TestSurface : public Surface { public: - TestSurface(const sp<IGraphicBufferProducer>& bufferProducer, - FenceToFenceTimeMap* fenceMap) - : Surface(bufferProducer), - mFakeSurfaceComposer(new FakeSurfaceComposer) { + TestSurface(const sp<IGraphicBufferProducer>& bufferProducer, FenceToFenceTimeMap* fenceMap) + : Surface(bufferProducer), + mFakeSurfaceComposer(new FakeSurfaceComposer), + mFakeSurfaceComposerAIDL(new FakeSurfaceComposerAIDL) { mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap); mFrameEventHistory.reset(mFakeFrameEventHistory); } @@ -1048,6 +1055,10 @@ public: return mFakeSurfaceComposer; } + sp<gui::ISurfaceComposer> composerServiceAIDL() const override { + return mFakeSurfaceComposerAIDL; + } + nsecs_t now() const override { return mNow; } @@ -1058,6 +1069,7 @@ public: public: sp<FakeSurfaceComposer> mFakeSurfaceComposer; + sp<FakeSurfaceComposerAIDL> mFakeSurfaceComposerAIDL; nsecs_t mNow = 0; // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, @@ -1070,20 +1082,30 @@ class GetFrameTimestampsTest : public ::testing::Test { protected: struct FenceAndFenceTime { explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap) - : mFence(new Fence), - mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} - sp<Fence> mFence { nullptr }; - std::shared_ptr<FenceTime> mFenceTime { nullptr }; + : mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} + + sp<Fence> mFence = sp<Fence>::make(); + std::shared_ptr<FenceTime> mFenceTime; }; + static CompositorTiming makeCompositorTiming(nsecs_t deadline = 1'000'000'000, + nsecs_t interval = 16'666'667, + nsecs_t presentLatency = 50'000'000) { + CompositorTiming timing; + timing.deadline = deadline; + timing.interval = interval; + timing.presentLatency = presentLatency; + return timing; + } + struct RefreshEvents { RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) - : mFenceMap(fenceMap), - kCompositorTiming( - {refreshStart, refreshStart + 1, refreshStart + 2 }), - kStartTime(refreshStart + 3), - kGpuCompositionDoneTime(refreshStart + 4), - kPresentTime(refreshStart + 5) {} + : mFenceMap(fenceMap), + kCompositorTiming( + makeCompositorTiming(refreshStart, refreshStart + 1, refreshStart + 2)), + kStartTime(refreshStart + 3), + kGpuCompositionDoneTime(refreshStart + 4), + kPresentTime(refreshStart + 5) {} void signalPostCompositeFences() { mFenceMap.signalAllForTest( @@ -1093,8 +1115,8 @@ protected: FenceToFenceTimeMap& mFenceMap; - FenceAndFenceTime mGpuCompositionDone { mFenceMap }; - FenceAndFenceTime mPresent { mFenceMap }; + FenceAndFenceTime mGpuCompositionDone{mFenceMap}; + FenceAndFenceTime mPresent{mFenceMap}; const CompositorTiming kCompositorTiming; @@ -1360,11 +1382,7 @@ TEST_F(GetFrameTimestampsTest, DefaultDisabled) { // This test verifies that the frame timestamps are retrieved if explicitly // enabled via native_window_enable_frame_timestamps. TEST_F(GetFrameTimestampsTest, EnabledSimple) { - CompositorTiming initialCompositorTiming { - 1000000000, // 1s deadline - 16666667, // 16ms interval - 50000000, // 50ms present latency - }; + const CompositorTiming initialCompositorTiming = makeCompositorTiming(); mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); @@ -1424,6 +1442,7 @@ TEST_F(GetFrameTimestampsTest, EnabledSimple) { TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { bool displayPresentSupported = true; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); + mSurface->mFakeSurfaceComposerAIDL->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; @@ -1435,6 +1454,7 @@ TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) { bool displayPresentSupported = false; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); + mSurface->mFakeSurfaceComposerAIDL->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; @@ -1501,11 +1521,7 @@ TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) { // This verifies the compositor timing is updated by refresh events // and piggy backed on a queue, dequeue, and enabling of timestamps.. TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { - CompositorTiming initialCompositorTiming { - 1000000000, // 1s deadline - 16666667, // 16ms interval - 50000000, // 50ms present latency - }; + const CompositorTiming initialCompositorTiming = makeCompositorTiming(); mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); @@ -1586,11 +1602,7 @@ TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { // This verifies the compositor deadline properly snaps to the the next // deadline based on the current time. TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { - CompositorTiming initialCompositorTiming { - 1000000000, // 1s deadline - 16666667, // 16ms interval - 50000000, // 50ms present latency - }; + const CompositorTiming initialCompositorTiming = makeCompositorTiming(); mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); @@ -2012,6 +2024,7 @@ TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) { TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { enableFrameTimestamps(); mSurface->mFakeSurfaceComposer->setSupportsPresent(false); + mSurface->mFakeSurfaceComposerAIDL->setSupportsPresent(false); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index 99658ccd4b..c51b244c50 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -71,7 +71,6 @@ TEST(WindowInfo, Parcelling) { i.applicationInfo.name = "ApplicationFooBar"; i.applicationInfo.token = new BBinder(); i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD; - i.isClone = true; Parcel p; i.writeToParcel(&p); @@ -102,7 +101,6 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); ASSERT_EQ(i.applicationInfo, i2.applicationInfo); - ASSERT_EQ(i.isClone, i2.isClone); } TEST(InputApplicationInfo, Parcelling) { diff --git a/libs/input/Android.bp b/libs/input/Android.bp index b2fec7917b..34ef7b4946 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -26,7 +26,7 @@ package { filegroup { name: "inputconstants_aidl", srcs: [ - "android/os/BlockUntrustedTouchesMode.aidl", + "android/hardware/input/InputDeviceCountryCode.aidl", "android/os/IInputConstants.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", @@ -89,7 +89,6 @@ cc_library { shared_libs: [ "libutils", "libbinder", - "libui", ], static_libs: [ diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 2b7483d27d..9e8ebf30c5 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -21,7 +21,9 @@ #include <cutils/compiler.h> #include <inttypes.h> #include <string.h> +#include <optional> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <cutils/compiler.h> @@ -34,7 +36,7 @@ #ifdef __linux__ #include <binder/Parcel.h> #endif -#ifdef __ANDROID__ +#if defined(__ANDROID__) #include <sys/random.h> #endif @@ -87,6 +89,8 @@ const char* motionClassificationToString(MotionClassification classification) { return "AMBIGUOUS_GESTURE"; case MotionClassification::DEEP_PRESS: return "DEEP_PRESS"; + case MotionClassification::TWO_FINGER_SWIPE: + return "TWO_FINGER_SWIPE"; } } @@ -110,15 +114,31 @@ const char* motionToolTypeToString(int32_t toolType) { } // --- IdGenerator --- +#if defined(__ANDROID__) +[[maybe_unused]] +#endif +static status_t +getRandomBytes(uint8_t* data, size_t size) { + int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ret == -1) { + return -errno; + } + + base::unique_fd fd(ret); + if (!base::ReadFully(fd, data, size)) { + return -errno; + } + return OK; +} + IdGenerator::IdGenerator(Source source) : mSource(source) {} int32_t IdGenerator::nextId() const { constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK; int32_t id = 0; -// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't -// use sequence number so just always return mSource. -#ifdef __ANDROID__ +#if defined(__ANDROID__) + // On device, prefer 'getrandom' to '/dev/urandom' because it's faster. constexpr size_t BUF_LEN = sizeof(id); size_t totalBytes = 0; while (totalBytes < BUF_LEN) { @@ -130,8 +150,17 @@ int32_t IdGenerator::nextId() const { } totalBytes += bytes; } +#else +#if defined(__linux__) + // On host, <sys/random.h> / GRND_NONBLOCK is not available + while (true) { + status_t result = getRandomBytes(reinterpret_cast<uint8_t*>(&id), sizeof(id)); + if (result == OK) { + break; + } + } +#endif // __linux__ #endif // __ANDROID__ - return (id & SEQUENCE_NUMBER_MASK) | static_cast<int32_t>(mSource); } @@ -210,6 +239,10 @@ bool isFromSource(uint32_t source, uint32_t test) { return (source & test) == test; } +bool isStylusToolType(uint32_t toolType) { + return toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || toolType == AMOTION_EVENT_TOOL_TYPE_ERASER; +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, @@ -410,14 +443,6 @@ bool PointerCoords::operator==(const PointerCoords& other) const { return true; } -void PointerCoords::copyFrom(const PointerCoords& other) { - bits = other.bits; - uint32_t count = BitSet64::count(bits); - for (uint32_t i = 0; i < count; i++) { - values[i] = other.values[i]; - } -} - void PointerCoords::transform(const ui::Transform& transform) { const vec2 xy = transform.transform(getXYValue()); setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); @@ -528,21 +553,21 @@ void MotionEvent::addSample( &pointerCoords[getPointerCount()]); } -int MotionEvent::getSurfaceRotation() const { +std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const { // The surface rotation is the rotation from the window's coordinate space to that of the // display. Since the event's transform takes display space coordinates to window space, the // returned surface rotation is the inverse of the rotation for the surface. switch (mTransform.getOrientation()) { case ui::Transform::ROT_0: - return DISPLAY_ORIENTATION_0; + return ui::ROTATION_0; case ui::Transform::ROT_90: - return DISPLAY_ORIENTATION_270; + return ui::ROTATION_270; case ui::Transform::ROT_180: - return DISPLAY_ORIENTATION_180; + return ui::ROTATION_180; case ui::Transform::ROT_270: - return DISPLAY_ORIENTATION_90; + return ui::ROTATION_90; default: - return -1; + return std::nullopt; } } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index a9089690b0..4751a7de8b 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -26,6 +26,7 @@ #include <input/InputEventLabels.h> using android::base::StringPrintf; +using android::hardware::input::InputDeviceCountryCode; namespace android { @@ -177,9 +178,11 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mAlias(other.mAlias), mIsExternal(other.mIsExternal), mHasMic(other.mHasMic), + mCountryCode(other.mCountryCode), mSources(other.mSources), mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap), + mSupportsUsi(other.mSupportsUsi), mHasVibrator(other.mHasVibrator), mHasBattery(other.mHasBattery), mHasButtonUnderPad(other.mHasButtonUnderPad), @@ -192,8 +195,8 @@ InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, - bool hasMic) { + const InputDeviceIdentifier& identifier, const std::string& alias, + bool isExternal, bool hasMic, InputDeviceCountryCode countryCode) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -201,12 +204,14 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mAlias = alias; mIsExternal = isExternal; mHasMic = hasMic; + mCountryCode = countryCode; mSources = 0; mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; mHasVibrator = false; mHasBattery = false; mHasButtonUnderPad = false; mHasSensor = false; + mSupportsUsi = false; mMotionRanges.clear(); mSensors.clear(); mLights.clear(); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index c0aa2e26a2..b78fae3027 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -23,6 +23,8 @@ namespace android { +// clang-format off + // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. #define KEYCODES_SEQUENCE \ @@ -314,7 +316,30 @@ namespace android { DEFINE_KEYCODE(REFRESH), \ DEFINE_KEYCODE(THUMBS_UP), \ DEFINE_KEYCODE(THUMBS_DOWN), \ - DEFINE_KEYCODE(PROFILE_SWITCH) + DEFINE_KEYCODE(PROFILE_SWITCH), \ + DEFINE_KEYCODE(VIDEO_APP_1), \ + DEFINE_KEYCODE(VIDEO_APP_2), \ + DEFINE_KEYCODE(VIDEO_APP_3), \ + DEFINE_KEYCODE(VIDEO_APP_4), \ + DEFINE_KEYCODE(VIDEO_APP_5), \ + DEFINE_KEYCODE(VIDEO_APP_6), \ + DEFINE_KEYCODE(VIDEO_APP_7), \ + DEFINE_KEYCODE(VIDEO_APP_8), \ + DEFINE_KEYCODE(FEATURED_APP_1), \ + DEFINE_KEYCODE(FEATURED_APP_2), \ + DEFINE_KEYCODE(FEATURED_APP_3), \ + DEFINE_KEYCODE(FEATURED_APP_4), \ + DEFINE_KEYCODE(DEMO_APP_1), \ + DEFINE_KEYCODE(DEMO_APP_2), \ + DEFINE_KEYCODE(DEMO_APP_3), \ + DEFINE_KEYCODE(DEMO_APP_4), \ + DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \ + DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \ + DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE), \ + DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \ + DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \ + DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \ + DEFINE_KEYCODE(STYLUS_BUTTON_TAIL) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. @@ -366,8 +391,9 @@ namespace android { DEFINE_AXIS(GENERIC_13), \ DEFINE_AXIS(GENERIC_14), \ DEFINE_AXIS(GENERIC_15), \ - DEFINE_AXIS(GENERIC_16) - + DEFINE_AXIS(GENERIC_16), \ + DEFINE_AXIS(GESTURE_X_OFFSET), \ + DEFINE_AXIS(GESTURE_Y_OFFSET) // NOTE: If you add new LEDs here, you must also add them to Input.h #define LEDS_SEQUENCE \ @@ -393,6 +419,8 @@ namespace android { DEFINE_FLAG(GESTURE), \ DEFINE_FLAG(WAKE) +// clang-format on + // --- InputEventLookup --- const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE}; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 61950522ff..8d8433b973 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -51,7 +51,7 @@ static const nsecs_t NANOS_PER_MS = 1000000; // Latency added during resampling. A few milliseconds doesn't hurt much but // reduces the impact of mispredicted touch positions. -static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; +const std::chrono::duration RESAMPLE_LATENCY = 5ms; // Minimum time difference between consecutive samples before attempting to resample. static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; @@ -721,7 +721,11 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC // --- InputConsumer --- InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel) - : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {} + : InputConsumer(channel, isTouchResamplingEnabled()) {} + +InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel, + bool enableTouchResampling) + : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {} InputConsumer::~InputConsumer() { } @@ -751,7 +755,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // Receive a fresh message. status_t result = mChannel->receiveMessage(&mMsg); if (result == OK) { - mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + const auto [_, inserted] = + mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, + mMsg.header.seq); } if (result) { // Consume the next batched event unless batches are being held for later. @@ -918,7 +925,7 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t sampleTime = frameTime; if (mResampleTouch) { - sampleTime -= RESAMPLE_LATENCY; + sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count(); } ssize_t split = findSampleNoLaterThan(batch, sampleTime); if (split < 0) { @@ -1166,6 +1173,11 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, return; } + if (current->eventTime == sampleTime) { + // Prevents having 2 events with identical times and coordinates. + return; + } + // Resample touch coordinates. History oldLastResample; oldLastResample.initializeFrom(touchState.lastResample); diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 2039fa6553..fa5c41f657 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -43,7 +43,6 @@ // Enables debug output for mapping. #define DEBUG_MAPPING 0 - namespace android { static const char* WHITESPACE = " \t\r"; @@ -93,6 +92,7 @@ KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : mType(other.mType), mLoadFileName(other.mLoadFileName), mLayoutOverlayApplied(other.mLayoutOverlayApplied), + mKeyRemapping(other.mKeyRemapping), mKeysByScanCode(other.mKeysByScanCode), mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { @@ -114,7 +114,7 @@ bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) { return false; } - if (mKeys.size() != other.mKeys.size() || + if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() || mKeysByScanCode.size() != other.mKeysByScanCode.size() || mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) { return false; @@ -131,22 +131,9 @@ bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { } } - for (size_t i = 0; i < mKeysByScanCode.size(); i++) { - if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) { - return false; - } - if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) { - return false; - } - } - - for (size_t i = 0; i < mKeysByUsageCode.size(); i++) { - if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) { - return false; - } - if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) { - return false; - } + if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode || + mKeysByUsageCode != other.mKeysByUsageCode) { + return false; } return true; @@ -258,14 +245,12 @@ void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { } } - for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) { - mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i), - overlay.mKeysByScanCode.valueAt(i)); + for (auto const& it : overlay.mKeysByScanCode) { + mKeysByScanCode.insert_or_assign(it.first, it.second); } - for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) { - mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i), - overlay.mKeysByUsageCode.valueAt(i)); + for (auto const& it : overlay.mKeysByUsageCode) { + mKeysByUsageCode.insert_or_assign(it.first, it.second); } mLayoutOverlayApplied = true; } @@ -304,9 +289,8 @@ char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { char16_t result = 0; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + const Behavior* behavior = getKeyBehavior(keyCode, metaState); + if (behavior != nullptr) { result = behavior->character; } #if DEBUG_MAPPING @@ -321,9 +305,8 @@ bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, outFallbackAction->metaState = 0; bool result = false; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + const Behavior* behavior = getKeyBehavior(keyCode, metaState); + if (behavior != nullptr) { if (behavior->fallbackKeyCode) { outFallbackAction->keyCode = behavior->fallbackKeyCode; outFallbackAction->metaState = metaState & ~behavior->metaState; @@ -347,12 +330,12 @@ char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_ // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. // However, if we find a perfect meta state match for one behavior then use that one. - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character) { + for (const Behavior& behavior : key->behaviors) { + if (behavior.character) { for (size_t i = 0; i < numChars; i++) { - if (behavior->character == chars[i]) { - result = behavior->character; - if ((behavior->metaState & metaState) == behavior->metaState) { + if (behavior.character == chars[i]) { + result = behavior.character; + if ((behavior.metaState & metaState) == behavior.metaState) { goto ExactMatch; } break; @@ -402,11 +385,26 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t return true; } +void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { + if (fromKeyCode == toKeyCode) { + mKeyRemapping.erase(fromKeyCode); +#if DEBUG_MAPPING + ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode); +#endif + return; + } + mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode); +#if DEBUG_MAPPING + ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode, + toKeyCode); +#endif +} + status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - *outKeyCode = mKeysByUsageCode.valueAt(index); + const auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + *outKeyCode = it->second; #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", scanCode, usageCode, *outKeyCode); @@ -415,9 +413,9 @@ status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* o } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - *outKeyCode = mKeysByScanCode.valueAt(index); + const auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + *outKeyCode = it->second; #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", scanCode, usageCode, *outKeyCode); @@ -433,46 +431,59 @@ status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* o return NAME_NOT_FOUND; } -void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState, - int32_t *outKeyCode, int32_t *outMetaState) const { - *outKeyCode = keyCode; - *outMetaState = metaState; +int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const { + int32_t toKeyCode = fromKeyCode; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + const auto it = mKeyRemapping.find(fromKeyCode); + if (it != mKeyRemapping.end()) { + toKeyCode = it->second; + } +#if DEBUG_MAPPING + ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode); +#endif + return toKeyCode; +} + +std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode, + int32_t fromMetaState) const { + int32_t toKeyCode = fromKeyCode; + int32_t toMetaState = fromMetaState; + + const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState); + if (behavior != nullptr) { if (behavior->replacementKeyCode) { - *outKeyCode = behavior->replacementKeyCode; - int32_t newMetaState = metaState & ~behavior->metaState; + toKeyCode = behavior->replacementKeyCode; + toMetaState = fromMetaState & ~behavior->metaState; // Reset dependent meta states. if (behavior->metaState & AMETA_ALT_ON) { - newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); + toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); } if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - newMetaState &= ~AMETA_ALT_ON; + toMetaState &= ~AMETA_ALT_ON; } if (behavior->metaState & AMETA_CTRL_ON) { - newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); + toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); } if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { - newMetaState &= ~AMETA_CTRL_ON; + toMetaState &= ~AMETA_CTRL_ON; } if (behavior->metaState & AMETA_SHIFT_ON) { - newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON); + toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON); } if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { - newMetaState &= ~AMETA_SHIFT_ON; + toMetaState &= ~AMETA_SHIFT_ON; } // ... and put universal bits back if needed - *outMetaState = normalizeMetaState(newMetaState); + toMetaState = normalizeMetaState(toMetaState); } } #if DEBUG_MAPPING - ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ " - "replacement keyCode=%d, replacement metaState=0x%08x.", - keyCode, metaState, *outKeyCode, *outMetaState); + ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ " + "replacement keyCode=%d, replacement metaState=0x%08x.", + fromKeyCode, fromMetaState, toKeyCode, toMetaState); #endif + return std::make_pair(toKeyCode, toMetaState); } bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { @@ -484,21 +495,17 @@ bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { return false; } -bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, - const Key** outKey, const Behavior** outBehavior) const { +const KeyCharacterMap::Behavior* KeyCharacterMap::getKeyBehavior(int32_t keyCode, + int32_t metaState) const { const Key* key; if (getKey(keyCode, &key)) { - const Behavior* behavior = key->firstBehavior; - while (behavior) { - if (matchesMetaState(metaState, behavior->metaState)) { - *outKey = key; - *outBehavior = behavior; - return true; + for (const Behavior& behavior : key->behaviors) { + if (matchesMetaState(metaState, behavior.metaState)) { + return &behavior; } - behavior = behavior->next; } } - return false; + return nullptr; } bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { @@ -543,12 +550,12 @@ bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMeta // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. const Behavior* found = nullptr; - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character == ch) { - found = behavior; + for (const Behavior& behavior : key->behaviors) { + if (behavior.character == ch) { + found = &behavior; } } - if (found) { + if (found != nullptr) { *outKeyCode = mKeys.keyAt(i); *outMetaState = found->metaState; return true; @@ -706,7 +713,6 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) key->number = number; map->mKeys.add(keyCode, key); - Behavior* lastBehavior = nullptr; while (parcel->readInt32()) { int32_t metaState = parcel->readInt32(); char16_t character = parcel->readInt32(); @@ -716,23 +722,30 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) return nullptr; } - Behavior* behavior = new Behavior(); - behavior->metaState = metaState; - behavior->character = character; - behavior->fallbackKeyCode = fallbackKeyCode; - behavior->replacementKeyCode = replacementKeyCode; - if (lastBehavior) { - lastBehavior->next = behavior; - } else { - key->firstBehavior = behavior; - } - lastBehavior = behavior; + key->behaviors.push_back({ + .metaState = metaState, + .character = character, + .fallbackKeyCode = fallbackKeyCode, + .replacementKeyCode = replacementKeyCode, + }); } if (parcel->errorCheck()) { return nullptr; } } + size_t numKeyRemapping = parcel->readInt32(); + if (parcel->errorCheck()) { + return nullptr; + } + for (size_t i = 0; i < numKeyRemapping; i++) { + int32_t key = parcel->readInt32(); + int32_t value = parcel->readInt32(); + map->mKeyRemapping.insert_or_assign(key, value); + if (parcel->errorCheck()) { + return nullptr; + } + } size_t numKeysByScanCode = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; @@ -740,7 +753,7 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) for (size_t i = 0; i < numKeysByScanCode; i++) { int32_t key = parcel->readInt32(); int32_t value = parcel->readInt32(); - map->mKeysByScanCode.add(key, value); + map->mKeysByScanCode.insert_or_assign(key, value); if (parcel->errorCheck()) { return nullptr; } @@ -752,7 +765,7 @@ std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) for (size_t i = 0; i < numKeysByUsageCode; i++) { int32_t key = parcel->readInt32(); int32_t value = parcel->readInt32(); - map->mKeysByUsageCode.add(key, value); + map->mKeysByUsageCode.insert_or_assign(key, value); if (parcel->errorCheck()) { return nullptr; } @@ -777,65 +790,42 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(keyCode); parcel->writeInt32(key->label); parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != nullptr; - behavior = behavior->next) { + for (const Behavior& behavior : key->behaviors) { parcel->writeInt32(1); - parcel->writeInt32(behavior->metaState); - parcel->writeInt32(behavior->character); - parcel->writeInt32(behavior->fallbackKeyCode); - parcel->writeInt32(behavior->replacementKeyCode); + parcel->writeInt32(behavior.metaState); + parcel->writeInt32(behavior.character); + parcel->writeInt32(behavior.fallbackKeyCode); + parcel->writeInt32(behavior.replacementKeyCode); } parcel->writeInt32(0); } + size_t numKeyRemapping = mKeyRemapping.size(); + parcel->writeInt32(numKeyRemapping); + for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) { + parcel->writeInt32(fromAndroidKeyCode); + parcel->writeInt32(toAndroidKeyCode); + } size_t numKeysByScanCode = mKeysByScanCode.size(); parcel->writeInt32(numKeysByScanCode); - for (size_t i = 0; i < numKeysByScanCode; i++) { - parcel->writeInt32(mKeysByScanCode.keyAt(i)); - parcel->writeInt32(mKeysByScanCode.valueAt(i)); + for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) { + parcel->writeInt32(fromScanCode); + parcel->writeInt32(toAndroidKeyCode); } size_t numKeysByUsageCode = mKeysByUsageCode.size(); parcel->writeInt32(numKeysByUsageCode); - for (size_t i = 0; i < numKeysByUsageCode; i++) { - parcel->writeInt32(mKeysByUsageCode.keyAt(i)); - parcel->writeInt32(mKeysByUsageCode.valueAt(i)); + for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) { + parcel->writeInt32(fromUsageCode); + parcel->writeInt32(toAndroidKeyCode); } } #endif // __linux__ // --- KeyCharacterMap::Key --- -KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(nullptr) { -} - -KeyCharacterMap::Key::Key(const Key& other) : - label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) { -} - -KeyCharacterMap::Key::~Key() { - Behavior* behavior = firstBehavior; - while (behavior) { - Behavior* next = behavior->next; - delete behavior; - behavior = next; - } -} - - -// --- KeyCharacterMap::Behavior --- - -KeyCharacterMap::Behavior::Behavior() : - next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { -} - -KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : nullptr), - metaState(other.metaState), character(other.character), - fallbackKeyCode(other.fallbackKeyCode), - replacementKeyCode(other.replacementKeyCode) { -} +KeyCharacterMap::Key::Key() : label(0), number(0) {} +KeyCharacterMap::Key::Key(const Key& other) + : label(other.label), number(other.number), behaviors(other.behaviors) {} // --- KeyCharacterMap::Parser --- @@ -992,9 +982,9 @@ status_t KeyCharacterMap::Parser::parseMapKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, int32_t>& map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + const auto it = map.find(code); + if (it != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -1013,7 +1003,7 @@ status_t KeyCharacterMap::Parser::parseMapKey() { ALOGD("Parsed map key %s: code=%d, keyCode=%d.", mapUsage ? "usage" : "scan code", code, keyCode); #endif - map.add(code, keyCode); + map.insert_or_assign(code, keyCode); return NO_ERROR; } @@ -1213,23 +1203,21 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { #endif break; case PROPERTY_META: { - for (Behavior* b = key->firstBehavior; b; b = b->next) { - if (b->metaState == property.metaState) { + for (const Behavior& b : key->behaviors) { + if (b.metaState == property.metaState) { ALOGE("%s: Duplicate key behavior for modifier.", mTokenizer->getLocation().string()); return BAD_VALUE; } } - Behavior* newBehavior = new Behavior(behavior); - newBehavior->metaState = property.metaState; - newBehavior->next = key->firstBehavior; - key->firstBehavior = newBehavior; -#if DEBUG_PARSER - ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d replace=%d.", - mKeyCode, - newBehavior->metaState, newBehavior->character, - newBehavior->fallbackKeyCode, newBehavior->replacementKeyCode); -#endif + Behavior newBehavior = behavior; + newBehavior.metaState = property.metaState; + key->behaviors.push_front(newBehavior); + ALOGD_IF(DEBUG_PARSER, + "Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d replace=%d.", + mKeyCode, key->behaviors.front().metaState, key->behaviors.front().character, + key->behaviors.front().fallbackKeyCode, + key->behaviors.front().replacementKeyCode); break; } } @@ -1242,8 +1230,8 @@ status_t KeyCharacterMap::Parser::finishKey(Key* key) { if (!key->number) { char16_t digit = 0; char16_t symbol = 0; - for (Behavior* b = key->firstBehavior; b; b = b->next) { - char16_t ch = b->character; + for (const Behavior& b : key->behaviors) { + char16_t ch = b.character; if (ch) { if (ch >= '0' && ch <= '9') { digit = ch; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index d6b4579a94..73710330d0 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -192,7 +192,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, } // Return pair of sensor type and sensor data index, for the input device abs code -base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) { +base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor( + int32_t absCode) const { auto it = mSensorsByAbsCode.find(absCode); if (it == mSensorsByAbsCode.end()) { ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode); diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index c3f5151fd1..3f8467d818 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -49,25 +49,23 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration) { // Use the configured key layout if available. if (deviceConfiguration) { - String8 keyLayoutName; - if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), - keyLayoutName)) { + std::string keyLayoutName; + if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) { status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " - "it was not found.", - deviceIdentifier.name.c_str(), keyLayoutName.string()); + "it was not found.", + deviceIdentifier.name.c_str(), keyLayoutName.c_str()); } } - String8 keyCharacterMapName; - if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), - keyCharacterMapName)) { + std::string keyCharacterMapName; + if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) { status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " - "map '%s' but it was not found.", - deviceIdentifier.name.c_str(), keyCharacterMapName.string()); + "map '%s' but it was not found.", + deviceIdentifier.name.c_str(), keyCharacterMapName.c_str()); } } @@ -165,7 +163,7 @@ bool isKeyboardSpecialFunction(const PropertyMap* config) { return false; } bool isSpecialFunction = false; - config->tryGetProperty(String8("keyboard.specialFunction"), isSpecialFunction); + config->tryGetProperty("keyboard.specialFunction", isSpecialFunction); return isSpecialFunction; } @@ -180,8 +178,7 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, if (deviceConfiguration) { bool builtIn = false; - if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) - && builtIn) { + if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) { return true; } } diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index a842166761..ed9ac9fc72 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PropertyMap" #include <input/PropertyMap.h> +#include <log/log.h> // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -39,25 +40,25 @@ void PropertyMap::clear() { mProperties.clear(); } -void PropertyMap::addProperty(const String8& key, const String8& value) { - mProperties.add(key, value); +void PropertyMap::addProperty(const std::string& key, const std::string& value) { + mProperties.emplace(key, value); } -bool PropertyMap::hasProperty(const String8& key) const { - return mProperties.indexOfKey(key) >= 0; +bool PropertyMap::hasProperty(const std::string& key) const { + return mProperties.find(key) != mProperties.end(); } -bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { - ssize_t index = mProperties.indexOfKey(key); - if (index < 0) { +bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const { + auto it = mProperties.find(key); + if (it == mProperties.end()) { return false; } - outValue = mProperties.valueAt(index); + outValue = it->second; return true; } -bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { +bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const { int32_t intValue; if (!tryGetProperty(key, intValue)) { return false; @@ -67,34 +68,34 @@ bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { return true; } -bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { - String8 stringValue; +bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const { + std::string stringValue; if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) { return false; } char* end; - int value = strtol(stringValue.string(), &end, 10); + int32_t value = static_cast<int32_t>(strtol(stringValue.c_str(), &end, 10)); if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(), - stringValue.string()); + ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.c_str(), + stringValue.c_str()); return false; } outValue = value; return true; } -bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { - String8 stringValue; +bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const { + std::string stringValue; if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) { return false; } char* end; - float value = strtof(stringValue.string(), &end); + float value = strtof(stringValue.c_str(), &end); if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(), - stringValue.string()); + ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.c_str(), + stringValue.c_str()); return false; } outValue = value; @@ -102,8 +103,8 @@ bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { } void PropertyMap::addAll(const PropertyMap* map) { - for (size_t i = 0; i < map->mProperties.size(); i++) { - mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + for (const auto& [key, value] : map->mProperties) { + mProperties.emplace(key, value); } } @@ -115,25 +116,24 @@ android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char Tokenizer* rawTokenizer; status_t status = Tokenizer::open(String8(filename), &rawTokenizer); - std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); if (status) { - ALOGE("Error %d opening property file %s.", status, filename); - } else { + return android::base::Error(-status) << "Could not open file: " << filename; + } #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(outMap.get(), tokenizer.get()); - status = parser.parse(); + std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); + Parser parser(outMap.get(), tokenizer.get()); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed property file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), + tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (status) { - return android::base::Error(BAD_VALUE) << "Could not parse " << filename; - } + if (status) { + return android::base::Error(BAD_VALUE) << "Could not parse " << filename; } + return std::move(outMap); } @@ -184,13 +184,13 @@ status_t PropertyMap::Parser::parse() { return BAD_VALUE; } - if (mMap->hasProperty(keyToken)) { + if (mMap->hasProperty(keyToken.string())) { ALOGE("%s: Duplicate property value for key '%s'.", mTokenizer->getLocation().string(), keyToken.string()); return BAD_VALUE; } - mMap->addProperty(keyToken, valueToken); + mMap->addProperty(keyToken.string(), valueToken.string()); } mTokenizer->nextLine(); diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp index afb97a1d47..d985dc1748 100755 --- a/libs/input/PropertyMap_fuzz.cpp +++ b/libs/input/PropertyMap_fuzz.cpp @@ -17,32 +17,22 @@ #include "android-base/file.h" #include "fuzzer/FuzzedDataProvider.h" #include "input/PropertyMap.h" -#include "utils/String8.h" static constexpr int MAX_FILE_SIZE = 256; static constexpr int MAX_STR_LEN = 2048; static constexpr int MAX_OPERATIONS = 1000; -static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>> +static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap&)>> operations = { - [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void { - propertyMap.getProperties(); - }, - [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void { + [](FuzzedDataProvider*, android::PropertyMap& propertyMap) -> void { propertyMap.clear(); }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { - std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); - android::String8 key = android::String8(keyStr.c_str()); - propertyMap.hasProperty(key); - }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { - std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); - android::String8 key = android::String8(keyStr.c_str()); - android::String8 out; + [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void { + std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + std::string out; propertyMap.tryGetProperty(key, out); }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void { + [](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void { TemporaryFile tf; // Generate file contents std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE); @@ -54,17 +44,15 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper } android::PropertyMap::load(tf.path); }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { - std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); - std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); - android::String8 key = android::String8(keyStr.c_str()); - android::String8 val = android::String8(valStr.c_str()); + [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void { + std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + std::string val = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); propertyMap.addProperty(key, val); }, }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider dataProvider(data, size); - android::PropertyMap propertyMap = android::PropertyMap(); + android::PropertyMap propertyMap; int opsRun = 0; while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp index c62e0985f1..c9393f4451 100644 --- a/libs/input/TouchVideoFrame.cpp +++ b/libs/input/TouchVideoFrame.cpp @@ -40,17 +40,20 @@ const std::vector<int16_t>& TouchVideoFrame::getData() const { return mData; } const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; } -void TouchVideoFrame::rotate(int32_t orientation) { +void TouchVideoFrame::rotate(ui::Rotation orientation) { switch (orientation) { - case DISPLAY_ORIENTATION_90: + case ui::ROTATION_90: rotateQuarterTurn(false /*clockwise*/); break; - case DISPLAY_ORIENTATION_180: + case ui::ROTATION_180: rotate180(); break; - case DISPLAY_ORIENTATION_270: + case ui::ROTATION_270: rotateQuarterTurn(true /*clockwise*/); break; + case ui::ROTATION_0: + // No need to rotate if there's no rotation. + break; } } diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 6e991e98bb..5c008b1158 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -37,6 +37,10 @@ VelocityControl::VelocityControl() { reset(); } +VelocityControlParameters& VelocityControl::getParameters() { + return mParameters; +} + void VelocityControl::setParameters(const VelocityControlParameters& parameters) { mParameters = parameters; reset(); @@ -44,8 +48,8 @@ void VelocityControl::setParameters(const VelocityControlParameters& parameters) void VelocityControl::reset() { mLastMovementTime = LLONG_MIN; - mRawPosition.x = 0; - mRawPosition.y = 0; + mRawPositionX = 0; + mRawPositionY = 0; mVelocityTracker.clear(); } @@ -61,17 +65,20 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { mLastMovementTime = eventTime; if (deltaX) { - mRawPosition.x += *deltaX; + mRawPositionX += *deltaX; } if (deltaY) { - mRawPosition.y += *deltaY; + mRawPositionY += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition}); + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), + {{AMOTION_EVENT_AXIS_X, {mRawPositionX}}, + {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}}); - float vx, vy; + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); float scale = mParameters.scale; - if (mVelocityTracker.getVelocity(0, &vx, &vy)) { - float speed = hypotf(vx, vy) * scale; + if (vx && vy) { + float speed = hypotf(*vx, *vy) * scale; if (speed >= mParameters.highThreshold) { // Apply full acceleration above the high speed threshold. scale *= mParameters.acceleration; @@ -85,10 +92,9 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (DEBUG_ACCELERATION) { ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, - vx, vy, speed, scale / mParameters.scale); + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); } } else { diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 7f427f2364..19b4684e4a 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -27,6 +27,8 @@ #include <utils/BitSet.h> #include <utils/Timers.h> +using std::literals::chrono_literals::operator""ms; + namespace android { /** @@ -53,12 +55,34 @@ const bool DEBUG_IMPULSE = // Nanoseconds per milliseconds. static const nsecs_t NANOS_PER_MS = 1000000; +// All axes supported for velocity tracking, mapped to their default strategies. +// Although other strategies are available for testing and comparison purposes, +// the default strategy is the one that applications will actually use. Be very careful +// when adjusting the default strategy because it can dramatically affect +// (often in a bad way) the user experience. +static const std::map<int32_t, VelocityTracker::Strategy> DEFAULT_STRATEGY_BY_AXIS = + {{AMOTION_EVENT_AXIS_X, VelocityTracker::Strategy::LSQ2}, + {AMOTION_EVENT_AXIS_Y, VelocityTracker::Strategy::LSQ2}, + {AMOTION_EVENT_AXIS_SCROLL, VelocityTracker::Strategy::IMPULSE}}; + +// Axes specifying location on a 2D plane (i.e. X and Y). +static const std::set<int32_t> PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}; + +// Axes whose motion values are differential values (i.e. deltas). +static const std::set<int32_t> DIFFERENTIAL_AXES = {AMOTION_EVENT_AXIS_SCROLL}; + // Threshold for determining that a pointer has stopped moving. // Some input devices do not send ACTION_MOVE events in the case where a pointer has // stopped. We need to detect this case so that we can accurately predict the // velocity after the pointer starts moving again. -static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; +static const std::chrono::duration ASSUME_POINTER_STOPPED_TIME = 40ms; +static std::string toString(std::chrono::nanoseconds t) { + std::stringstream stream; + stream.precision(1); + stream << std::fixed << std::chrono::duration<float, std::milli>(t).count() << " ms"; + return stream.str(); +} static float vectorDot(const float* a, const float* b, uint32_t m) { float r = 0; @@ -118,46 +142,46 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- VelocityTracker::VelocityTracker(const Strategy strategy) - : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - // Configure the strategy. - if (!configureStrategy(strategy)) { - ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); - if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32 - "'!", - strategy); - } - } -} + : mLastEventTime(0), + mCurrentPointerIdBits(0), + mActivePointerId(-1), + mOverrideStrategy(strategy) {} VelocityTracker::~VelocityTracker() { } -bool VelocityTracker::configureStrategy(Strategy strategy) { - if (strategy == VelocityTracker::Strategy::DEFAULT) { - mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); +bool VelocityTracker::isAxisSupported(int32_t axis) { + return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end(); +} + +void VelocityTracker::configureStrategy(int32_t axis) { + const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end(); + + std::unique_ptr<VelocityTrackerStrategy> createdStrategy; + if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) { + createdStrategy = createStrategy(mOverrideStrategy, isDifferentialAxis /* deltaValues */); } else { - mStrategy = createStrategy(strategy); + createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis), + isDifferentialAxis /* deltaValues */); } - return mStrategy != nullptr; + + LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr, + "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis); + mConfiguredStrategies[axis] = std::move(createdStrategy); } std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( - VelocityTracker::Strategy strategy) { + VelocityTracker::Strategy strategy, bool deltaValues) { switch (strategy) { case VelocityTracker::Strategy::IMPULSE: - if (DEBUG_STRATEGY) { - ALOGI("Initializing impulse strategy"); - } - return std::make_unique<ImpulseVelocityTrackerStrategy>(); + ALOGI_IF(DEBUG_STRATEGY, "Initializing impulse strategy"); + return std::make_unique<ImpulseVelocityTrackerStrategy>(deltaValues); case VelocityTracker::Strategy::LSQ1: return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1); case VelocityTracker::Strategy::LSQ2: - if (DEBUG_STRATEGY && !DEBUG_IMPULSE) { - ALOGI("Initializing lsq2 strategy"); - } + ALOGI_IF(DEBUG_STRATEGY && !DEBUG_IMPULSE, "Initializing lsq2 strategy"); return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2); case VelocityTracker::Strategy::LSQ3: @@ -197,8 +221,7 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( void VelocityTracker::clear() { mCurrentPointerIdBits.clear(); mActivePointerId = -1; - - mStrategy->clear(); + mConfiguredStrategies.clear(); } void VelocityTracker::clearPointers(BitSet32 idBits) { @@ -209,27 +232,25 @@ void VelocityTracker::clearPointers(BitSet32 idBits) { mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; } - mStrategy->clearPointers(idBits); + for (const auto& [_, strategy] : mConfiguredStrategies) { + strategy->clearPointers(idBits); + } } void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { - LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(), - "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", - idBits.count(), positions.size()); + const std::map<int32_t /*axis*/, std::vector<float>>& positions) { while (idBits.count() > MAX_POINTERS) { idBits.clearLastMarkedBit(); } - if ((mCurrentPointerIdBits.value & idBits.value) - && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) { - if (DEBUG_VELOCITY) { - ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.", - (eventTime - mLastEventTime) * 0.000001f); - } + if ((mCurrentPointerIdBits.value & idBits.value) && + std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) { + ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.", + toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str()); + // We have not received any movements for too long. Assume that all pointers // have stopped. - mStrategy->clear(); + mConfiguredStrategies.clear(); } mLastEventTime = eventTime; @@ -238,29 +259,40 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); } - mStrategy->addMovement(eventTime, idBits, positions); + for (const auto& [axis, positionValues] : positions) { + LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(), + "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", + idBits.count(), positionValues.size()); + if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { + configureStrategy(axis); + } + mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues); + } if (DEBUG_VELOCITY) { ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d", eventTime, idBits.value, mActivePointerId); - for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(id, &estimator); - ALOGD(" %d: position (%0.3f, %0.3f), " - "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", - id, positions[index].x, positions[index].y, int(estimator.degree), - vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(), - vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(), - estimator.confidence); + for (const auto& positionsEntry : positions) { + for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { + uint32_t id = iterBits.firstMarkedBit(); + uint32_t index = idBits.getIndexOfBit(id); + iterBits.clearBit(id); + Estimator estimator; + getEstimator(positionsEntry.first, id, &estimator); + ALOGD(" %d: axis=%d, position=%0.3f, " + "estimator (degree=%d, coeff=%s, confidence=%f)", + id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree), + vectorToString(estimator.coeff, estimator.degree + 1).c_str(), + estimator.confidence); + } } } } void VelocityTracker::addMovement(const MotionEvent* event) { + // Stores data about which axes to process based on the incoming motion event. + std::set<int32_t> axesToProcess; int32_t actionMasked = event->getActionMasked(); switch (actionMasked) { @@ -268,6 +300,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { case AMOTION_EVENT_ACTION_HOVER_ENTER: // Clear all pointers on down before adding the new movement. clear(); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; case AMOTION_EVENT_ACTION_POINTER_DOWN: { // Start a new movement trace for a pointer that just went down. @@ -276,13 +309,27 @@ void VelocityTracker::addMovement(const MotionEvent* event) { BitSet32 downIdBits; downIdBits.markBit(event->getPointerId(event->getActionIndex())); clearPointers(downIdBits); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; } case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_HOVER_MOVE: + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; - default: - // Ignore all other actions because they do not convey any new information about + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: { + std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); + if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { + ALOGD_IF(DEBUG_VELOCITY, + "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", + toString(delaySinceLastEvent).c_str()); + // We have not received any movements for too long. Assume that all pointers + // have stopped. + for (int32_t axis : PLANAR_AXES) { + mConfiguredStrategies.erase(axis); + } + } + // These actions because they do not convey any new information about // pointer movement. We also want to preserve the last known velocity of the pointers. // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position // of the pointers that went up. ACTION_POINTER_UP does include the new position of @@ -292,6 +339,13 @@ void VelocityTracker::addMovement(const MotionEvent* event) { // before adding the movement. return; } + case AMOTION_EVENT_ACTION_SCROLL: + axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); + break; + default: + // Ignore all other actions. + return; + } size_t pointerCount = event->getPointerCount(); if (pointerCount > MAX_POINTERS) { @@ -308,62 +362,74 @@ void VelocityTracker::addMovement(const MotionEvent* event) { pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); } - std::vector<Position> positions; - positions.resize(pointerCount); + std::map<int32_t, std::vector<float>> positions; + for (int32_t axis : axesToProcess) { + positions[axis].resize(pointerCount); + } size_t historySize = event->getHistorySize(); for (size_t h = 0; h <= historySize; h++) { nsecs_t eventTime = event->getHistoricalEventTime(h); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getHistoricalX(i, h); - positions[index].y = event->getHistoricalY(i, h); + for (int32_t axis : axesToProcess) { + for (size_t i = 0; i < pointerCount; i++) { + positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h); + } } addMovement(eventTime, idBits, positions); } } -bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { +std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const { Estimator estimator; - if (getEstimator(id, &estimator) && estimator.degree >= 1) { - *outVx = estimator.xCoeff[1]; - *outVy = estimator.yCoeff[1]; - return true; + bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1; + if (validVelocity) { + return estimator.coeff[1]; } - *outVx = 0; - *outVy = 0; - return false; + return {}; } -bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { - return mStrategy->getEstimator(id, outEstimator); +VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t units, + float maxVelocity) { + ComputedVelocity computedVelocity; + for (const auto& [axis, _] : mConfiguredStrategies) { + BitSet32 copyIdBits = BitSet32(mCurrentPointerIdBits); + while (!copyIdBits.isEmpty()) { + uint32_t id = copyIdBits.clearFirstMarkedBit(); + std::optional<float> velocity = getVelocity(axis, id); + if (velocity) { + float adjustedVelocity = + std::clamp(*velocity * units / 1000, -maxVelocity, maxVelocity); + computedVelocity.addVelocity(axis, id, adjustedVelocity); + } + } + } + return computedVelocity; } +bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const { + const auto& it = mConfiguredStrategies.find(axis); + if (it == mConfiguredStrategies.end()) { + return false; + } + return it->second->getEstimator(id, outEstimator); +} // --- LeastSquaresVelocityTrackerStrategy --- -LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( - uint32_t degree, Weighting weighting) : - mDegree(degree), mWeighting(weighting) { - clear(); -} +LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, + Weighting weighting) + : mDegree(degree), mWeighting(weighting), mIndex(0) {} LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { } -void LeastSquaresVelocityTrackerStrategy::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); -} - void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } -void LeastSquaresVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -438,10 +504,10 @@ void LeastSquaresVelocityTrackerStrategy::addMovement( static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y, const std::vector<float>& w, uint32_t n, float* outB, float* outDet) { const size_t m = x.size(); - if (DEBUG_STRATEGY) { - ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), - vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str()); - } + + ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), + vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str()); + LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes"); // Expand the X vector to a matrix A, pre-multiplied by the weights. @@ -452,9 +518,9 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo a[i][h] = a[i - 1][h] * x[h]; } } - if (DEBUG_STRATEGY) { - ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str()); - } + + ALOGD_IF(DEBUG_STRATEGY, " - a=%s", + matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str()); // Apply the Gram-Schmidt process to A to obtain its QR decomposition. float q[n][m]; // orthonormal basis, column-major order @@ -473,9 +539,7 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo float norm = vectorNorm(&q[j][0], m); if (norm < 0.000001f) { // vectors are linearly dependent or zero so no solution - if (DEBUG_STRATEGY) { - ALOGD(" - no solution, norm=%f", norm); - } + ALOGD_IF(DEBUG_STRATEGY, " - no solution, norm=%f", norm); return false; } @@ -518,9 +582,8 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo } outB[i] /= r[i][i]; } - if (DEBUG_STRATEGY) { - ALOGD(" - b=%s", vectorToString(outB, n).c_str()); - } + + ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB, n).c_str()); // Calculate the coefficient of determination as 1 - (SSerr / SStot) where // SSerr is the residual sum of squares (variance of the error), @@ -546,11 +609,11 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo sstot += w[h] * w[h] * var * var; } *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; - if (DEBUG_STRATEGY) { - ALOGD(" - sserr=%f", sserr); - ALOGD(" - sstot=%f", sstot); - ALOGD(" - det=%f", *outDet); - } + + ALOGD_IF(DEBUG_STRATEGY, " - sserr=%f", sserr); + ALOGD_IF(DEBUG_STRATEGY, " - sstot=%f", sstot); + ALOGD_IF(DEBUG_STRATEGY, " - det=%f", *outDet); + return true; } @@ -613,8 +676,7 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - std::vector<float> x; - std::vector<float> y; + std::vector<float> positions; std::vector<float> w; std::vector<float> time; @@ -631,15 +693,13 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, break; } - const VelocityTracker::Position& position = movement.getPosition(id); - x.push_back(position.x); - y.push_back(position.y); + positions.push_back(movement.getPosition(id)); w.push_back(chooseWeight(index)); time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (x.size() < HISTORY_SIZE); + } while (positions.size() < HISTORY_SIZE); - const size_t m = x.size(); + const size_t m = positions.size(); if (m == 0) { return false; // no data } @@ -652,39 +712,36 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, if (degree == 2 && mWeighting == WEIGHTING_NONE) { // Optimize unweighted, quadratic polynomial fit - std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x); - std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y); - if (xCoeff && yCoeff) { + std::optional<std::array<float, 3>> coeff = + solveUnweightedLeastSquaresDeg2(time, positions); + if (coeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; outEstimator->confidence = 1; for (size_t i = 0; i <= outEstimator->degree; i++) { - outEstimator->xCoeff[i] = (*xCoeff)[i]; - outEstimator->yCoeff[i] = (*yCoeff)[i]; + outEstimator->coeff[i] = (*coeff)[i]; } return true; } } else if (degree >= 1) { // General case for an Nth degree polynomial fit - float xdet, ydet; + float det; uint32_t n = degree + 1; - if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) && - solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) { + if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = degree; - outEstimator->confidence = xdet * ydet; - if (DEBUG_STRATEGY) { - ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", - int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(), - vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence); - } + outEstimator->confidence = det; + + ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f", + int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(), + outEstimator->confidence); + return true; } } // No velocity data available for this pointer, but we do have its current position. - outEstimator->xCoeff[0] = x[0]; - outEstimator->yCoeff[0] = y[0]; + outEstimator->coeff[0] = positions[0]; outEstimator->time = newestMovement.eventTime; outEstimator->degree = 0; outEstimator->confidence = 1; @@ -768,26 +825,21 @@ IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { } -void IntegratingVelocityTrackerStrategy::clear() { - mPointerIdBits.clear(); -} - void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mPointerIdBits.value &= ~idBits.value; } -void IntegratingVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { uint32_t index = 0; for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { uint32_t id = iterIdBits.clearFirstMarkedBit(); State& state = mPointerState[id]; - const VelocityTracker::Position& position = positions[index++]; + const float position = positions[index++]; if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position.x, position.y); + updateState(state, eventTime, position); } else { - initState(state, eventTime, position.x, position.y); + initState(state, eventTime, position); } } @@ -807,21 +859,18 @@ bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, return false; } -void IntegratingVelocityTrackerStrategy::initState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { +void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime, + float pos) const { state.updateTime = eventTime; state.degree = 0; - state.xpos = xpos; - state.xvel = 0; - state.xaccel = 0; - state.ypos = ypos; - state.yvel = 0; - state.yaccel = 0; + state.pos = pos; + state.accel = 0; + state.vel = 0; } -void IntegratingVelocityTrackerStrategy::updateState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { +void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime, + float pos) const { const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds @@ -832,34 +881,26 @@ void IntegratingVelocityTrackerStrategy::updateState(State& state, float dt = (eventTime - state.updateTime) * 0.000000001f; state.updateTime = eventTime; - float xvel = (xpos - state.xpos) / dt; - float yvel = (ypos - state.ypos) / dt; + float vel = (pos - state.pos) / dt; if (state.degree == 0) { - state.xvel = xvel; - state.yvel = yvel; + state.vel = vel; state.degree = 1; } else { float alpha = dt / (FILTER_TIME_CONSTANT + dt); if (mDegree == 1) { - state.xvel += (xvel - state.xvel) * alpha; - state.yvel += (yvel - state.yvel) * alpha; + state.vel += (vel - state.vel) * alpha; } else { - float xaccel = (xvel - state.xvel) / dt; - float yaccel = (yvel - state.yvel) / dt; + float accel = (vel - state.vel) / dt; if (state.degree == 1) { - state.xaccel = xaccel; - state.yaccel = yaccel; + state.accel = accel; state.degree = 2; } else { - state.xaccel += (xaccel - state.xaccel) * alpha; - state.yaccel += (yaccel - state.yaccel) * alpha; + state.accel += (accel - state.accel) * alpha; } - state.xvel += (state.xaccel * dt) * alpha; - state.yvel += (state.yaccel * dt) * alpha; + state.vel += (state.accel * dt) * alpha; } } - state.xpos = xpos; - state.ypos = ypos; + state.pos = pos; } void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, @@ -867,37 +908,26 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, outEstimator->time = state.updateTime; outEstimator->confidence = 1.0f; outEstimator->degree = state.degree; - outEstimator->xCoeff[0] = state.xpos; - outEstimator->xCoeff[1] = state.xvel; - outEstimator->xCoeff[2] = state.xaccel / 2; - outEstimator->yCoeff[0] = state.ypos; - outEstimator->yCoeff[1] = state.yvel; - outEstimator->yCoeff[2] = state.yaccel / 2; + outEstimator->coeff[0] = state.pos; + outEstimator->coeff[1] = state.vel; + outEstimator->coeff[2] = state.accel / 2; } // --- LegacyVelocityTrackerStrategy --- -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { - clear(); -} +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {} LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } -void LegacyVelocityTrackerStrategy::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); -} - void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } -void LegacyVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (++mIndex == HISTORY_SIZE) { mIndex = 0; } @@ -945,12 +975,11 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, // overestimate the velocity at that time point. Most samples might be measured // 16ms apart but some consecutive samples could be only 0.5sm apart because // the hardware or driver reports them irregularly or in bursts. - float accumVx = 0; - float accumVy = 0; + float accumV = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; const Movement& oldestMovement = mMovements[oldestIndex]; - const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); + float oldestPosition = oldestMovement.getPosition(id); nsecs_t lastDuration = 0; while (numTouches-- > 1) { @@ -964,26 +993,22 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { - const VelocityTracker::Position& position = movement.getPosition(id); + float position = movement.getPosition(id); float scale = 1000000000.0f / duration; // one over time delta in seconds - float vx = (position.x - oldestPosition.x) * scale; - float vy = (position.y - oldestPosition.y) * scale; - accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); - accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); + float v = (position - oldestPosition) * scale; + accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration); lastDuration = duration; samplesUsed += 1; } } // Report velocity. - const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); + float newestPosition = newestMovement.getPosition(id); outEstimator->time = newestMovement.eventTime; outEstimator->confidence = 1; - outEstimator->xCoeff[0] = newestPosition.x; - outEstimator->yCoeff[0] = newestPosition.y; + outEstimator->coeff[0] = newestPosition; if (samplesUsed) { - outEstimator->xCoeff[1] = accumVx; - outEstimator->yCoeff[1] = accumVy; + outEstimator->coeff[1] = accumV; outEstimator->degree = 1; } else { outEstimator->degree = 0; @@ -993,26 +1018,19 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, // --- ImpulseVelocityTrackerStrategy --- -ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() { - clear(); -} +ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues) + : mDeltaValues(deltaValues), mIndex(0) {} ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } -void ImpulseVelocityTrackerStrategy::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); -} - void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } -void ImpulseVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -1109,7 +1127,8 @@ static float kineticEnergyToVelocity(float work) { return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2; } -static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) { +static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count, + bool deltaValues) { // The input should be in reversed time order (most recent sample at index i=0) // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function static constexpr float SECONDS_PER_NANO = 1E-9; @@ -1120,12 +1139,26 @@ static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t c if (t[1] > t[0]) { // Algorithm will still work, but not perfectly ALOGE("Samples provided to calculateImpulseVelocity in the wrong order"); } + + // If the data values are delta values, we do not have to calculate deltas here. + // We can use the delta values directly, along with the calculated time deltas. + // Since the data value input is in reversed time order: + // [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1]) + // [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1]) + // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4]. + // Since the input is in reversed time order, the input values for this function would be + // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively. + // + // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1} + // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4} + if (count == 2) { // if 2 points, basic linear calculation if (t[1] == t[0]) { ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]); return 0; } - return (x[1] - x[0]) / (SECONDS_PER_NANO * (t[1] - t[0])); + const float deltaX = deltaValues ? -x[0] : x[1] - x[0]; + return deltaX / (SECONDS_PER_NANO * (t[1] - t[0])); } // Guaranteed to have at least 3 points here float work = 0; @@ -1135,7 +1168,8 @@ static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t c continue; } float vprev = kineticEnergyToVelocity(work); // v[i-1] - float vcurr = (x[i] - x[i-1]) / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i] + const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1]; + float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i] work += (vcurr - vprev) * fabsf(vcurr); if (i == count - 1) { work *= 0.5; // initial condition, case 2) above @@ -1149,8 +1183,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; + float positions[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting size_t index = mIndex; @@ -1166,9 +1199,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, break; } - const VelocityTracker::Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; + positions[m] = movement.getPosition(id); time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); @@ -1176,32 +1207,30 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, if (m == 0) { return false; // no data } - outEstimator->xCoeff[0] = 0; - outEstimator->yCoeff[0] = 0; - outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m); - outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m); - outEstimator->xCoeff[2] = 0; - outEstimator->yCoeff[2] = 0; + outEstimator->coeff[0] = 0; + outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); + outEstimator->coeff[2] = 0; + outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; // similar results to 2nd degree fit outEstimator->confidence = 1; - if (DEBUG_STRATEGY) { - ALOGD("velocity: (%.1f, %.1f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]); - } + + ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]); + if (DEBUG_IMPULSE) { // TODO(b/134179997): delete this block once the switch to 'impulse' is complete. - // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons + // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons. + // X axis chosen arbitrarily for velocity comparisons. VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2); BitSet32 idBits; const uint32_t pointerId = 0; idBits.markBit(pointerId); for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], idBits, {{x[i], y[i]}}); + lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}}); } - float outVx = 0, outVy = 0; - const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy); - if (computed) { - ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy); + std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); + if (v) { + ALOGD("lsq2 velocity: %.1f", *v); } else { ALOGD("lsq2 velocity: could not compute velocity"); } diff --git a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl new file mode 100644 index 0000000000..6bb1a60dda --- /dev/null +++ b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +/** + * Constant for HID country code declared by a HID device. These constants are declared as AIDL to + * be used by java and native input code. + * + * @hide + */ +@Backing(type="int") +enum InputDeviceCountryCode { + /** + * Used as default value where country code is not set in the device HID descriptor + */ + INVALID = -1, + + /** + * Used as default value when country code is not supported by the HID device. The HID + * descriptor sets "00" as the country code in this case. + */ + NOT_SUPPORTED = 0, + + /** + * Arabic + */ + ARABIC = 1, + + /** + * Belgian + */ + BELGIAN = 2, + + /** + * Canadian (Bilingual) + */ + CANADIAN_BILINGUAL = 3, + + /** + * Canadian (French) + */ + CANADIAN_FRENCH = 4, + + /** + * Czech Republic + */ + CZECH_REPUBLIC = 5, + + /** + * Danish + */ + DANISH = 6, + + /** + * Finnish + */ + FINNISH = 7, + + /** + * French + */ + FRENCH = 8, + + /** + * German + */ + GERMAN = 9, + + /** + * Greek + */ + GREEK = 10, + + /** + * Hebrew + */ + HEBREW = 11, + + /** + * Hungary + */ + HUNGARY = 12, + + /** + * International (ISO) + */ + INTERNATIONAL = 13, + + /** + * Italian + */ + ITALIAN = 14, + + /** + * Japan (Katakana) + */ + JAPAN = 15, + + /** + * Korean + */ + KOREAN = 16, + + /** + * Latin American + */ + LATIN_AMERICAN = 17, + + /** + * Netherlands (Dutch) + */ + DUTCH = 18, + + /** + * Norwegian + */ + NORWEGIAN = 19, + + /** + * Persian + */ + PERSIAN = 20, + + /** + * Poland + */ + POLAND = 21, + + /** + * Portuguese + */ + PORTUGUESE = 22, + + /** + * Russia + */ + RUSSIA = 23, + + /** + * Slovakia + */ + SLOVAKIA = 24, + + /** + * Spanish + */ + SPANISH = 25, + + /** + * Swedish + */ + SWEDISH = 26, + + /** + * Swiss (French) + */ + SWISS_FRENCH = 27, + + /** + * Swiss (German) + */ + SWISS_GERMAN = 28, + + /** + * Switzerland + */ + SWITZERLAND = 29, + + /** + * Taiwan + */ + TAIWAN = 30, + + /** + * Turkish_Q + */ + TURKISH_Q = 31, + + /** + * UK + */ + UK = 32, + + /** + * US + */ + US = 33, + + /** + * Yugoslavia + */ + YUGOSLAVIA = 34, + + /** + * Turkish_F + */ + TURKISH_F = 35, +}
\ No newline at end of file diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl deleted file mode 100644 index 9504e993f8..0000000000 --- a/libs/input/android/os/BlockUntrustedTouchesMode.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2020, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - - -/** - * Block untrusted touches feature mode. - * - * @hide - */ -@Backing(type="int") -enum BlockUntrustedTouchesMode { - /** Feature is off. */ - DISABLED, - - /** Untrusted touches are flagged but not blocked. */ - PERMISSIVE, - - /** Untrusted touches are blocked. */ - BLOCK -} diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 5ce10a4a50..dab843b48f 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -35,6 +35,14 @@ interface IInputConstants const int INVALID_INPUT_EVENT_ID = 0; /** + * Every input device has an id. This constant value is used when a valid input device id is not + * available. + * The virtual keyboard uses -1 as the input device id. Therefore, we use -2 as the value for + * an invalid input device. + */ + const int INVALID_INPUT_DEVICE_ID = -2; + + /** * The input event was injected from accessibility. Used in policyFlags for input event * injection. */ diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 6d1b3967f7..4e644fff06 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -144,4 +144,10 @@ enum InputConfig { * It is not valid to set this configuration if {@link #TRUSTED_OVERLAY} is not set. */ INTERCEPTS_STYLUS = 1 << 15, + + /** + * The window is a clone of another window. This may be treated differently since there's + * likely a duplicate window with the same client token, but different bounds. + */ + CLONE = 1 << 16, } diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index d947cd99e8..5aae37dae8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -10,12 +10,14 @@ package { cc_test { name: "libinput_tests", + host_supported: true, srcs: [ "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", "VerifiedInputEvent_test.cpp", @@ -23,6 +25,7 @@ cc_test { static_libs: [ "libgui_window_info_static", "libinput", + "libui-types", ], cflags: [ "-Wall", @@ -34,11 +37,13 @@ cc_test { "libbinder", "libcutils", "liblog", - "libui", "libutils", "libvintf", ], data: ["data/*"], + test_options: { + unit_test: true, + }, test_suites: ["device-tests"], } @@ -59,7 +64,6 @@ cc_library_static { "libcutils", "libutils", "libbinder", - "libui", "libbase", ], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index e872fa442b..2344463241 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -65,6 +65,9 @@ protected: } void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device."; +#endif loadKeyLayout("Generic"); loadKeyCharacterMap("Generic"); } @@ -131,6 +134,9 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { } TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "Can't check kernel configs on host"; +#endif std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl"; base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; @@ -139,6 +145,9 @@ TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { } TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "Can't check kernel configs on host"; +#endif std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl"; base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 05bc0bcbe8..70e4fda662 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -16,17 +16,10 @@ #include "TestHelpers.h" -#include <unistd.h> -#include <sys/mman.h> -#include <time.h> - #include <attestation/HmacKeyManager.h> -#include <cutils/ashmem.h> #include <gtest/gtest.h> #include <gui/constants.h> #include <input/InputTransport.h> -#include <utils/StopWatch.h> -#include <utils/Timers.h> using android::base::Result; diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp new file mode 100644 index 0000000000..c09a8e9358 --- /dev/null +++ b/libs/input/tests/TouchResampling_test.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2022 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 "TestHelpers.h" + +#include <chrono> +#include <vector> + +#include <attestation/HmacKeyManager.h> +#include <gtest/gtest.h> +#include <input/InputTransport.h> + +using namespace std::chrono_literals; + +namespace android { + +struct Pointer { + int32_t id; + float x; + float y; +}; + +struct InputEventEntry { + std::chrono::nanoseconds eventTime; + std::vector<Pointer> pointers; + int32_t action; +}; + +class TouchResamplingTest : public testing::Test { +protected: + std::unique_ptr<InputPublisher> mPublisher; + std::unique_ptr<InputConsumer> mConsumer; + PreallocatedInputEventFactory mEventFactory; + + uint32_t mSeq = 1; + + void SetUp() override { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result); + + mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); + mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel), + true /* enableTouchResampling */); + } + + status_t publishSimpleMotionEventWithCoords(int32_t action, nsecs_t eventTime, + const std::vector<PointerProperties>& properties, + const std::vector<PointerCoords>& coords); + void publishSimpleMotionEvent(int32_t action, nsecs_t eventTime, + const std::vector<Pointer>& pointers); + void publishInputEventEntries(const std::vector<InputEventEntry>& entries); + void consumeInputEventEntries(const std::vector<InputEventEntry>& entries, + std::chrono::nanoseconds frameTime); + void receiveResponseUntilSequence(uint32_t seq); +}; + +status_t TouchResamplingTest::publishSimpleMotionEventWithCoords( + int32_t action, nsecs_t eventTime, const std::vector<PointerProperties>& properties, + const std::vector<PointerCoords>& coords) { + const ui::Transform identityTransform; + const nsecs_t downTime = 0; + + if (action == AMOTION_EVENT_ACTION_DOWN && eventTime != 0) { + ADD_FAILURE() << "Downtime should be equal to 0 (hardcoded for convenience)"; + } + return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), 1 /*deviceId*/, + AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, INVALID_HMAC, + action, 0 /*actionButton*/, 0 /*flags*/, 0 /*edgeFlags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + downTime, eventTime, properties.size(), properties.data(), + coords.data()); +} + +void TouchResamplingTest::publishSimpleMotionEvent(int32_t action, nsecs_t eventTime, + const std::vector<Pointer>& pointers) { + std::vector<PointerProperties> properties; + std::vector<PointerCoords> coords; + + for (const Pointer& pointer : pointers) { + properties.push_back({}); + properties.back().clear(); + properties.back().id = pointer.id; + properties.back().toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + coords.push_back({}); + coords.back().clear(); + coords.back().setAxisValue(AMOTION_EVENT_AXIS_X, pointer.x); + coords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, pointer.y); + } + + status_t result = publishSimpleMotionEventWithCoords(action, eventTime, properties, coords); + ASSERT_EQ(OK, result); +} + +/** + * Each entry is published separately, one entry at a time. As a result, action is used here + * on a per-entry basis. + */ +void TouchResamplingTest::publishInputEventEntries(const std::vector<InputEventEntry>& entries) { + for (const InputEventEntry& entry : entries) { + publishSimpleMotionEvent(entry.action, entry.eventTime.count(), entry.pointers); + } +} + +/** + * Inside the publisher, read responses repeatedly until the desired sequence number is returned. + * + * Sometimes, when you call 'sendFinishedSignal', you would be finishing a batch which is comprised + * of several input events. As a result, consumer will generate multiple 'finish' signals on your + * behalf. + * + * In this function, we call 'receiveConsumerResponse' in a loop until the desired sequence number + * is returned. + */ +void TouchResamplingTest::receiveResponseUntilSequence(uint32_t seq) { + size_t consumedEvents = 0; + while (consumedEvents < 100) { + android::base::Result<InputPublisher::ConsumerResponse> response = + mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(response.ok()); + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*response)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*response); + ASSERT_TRUE(finish.handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + if (finish.seq == seq) { + return; + } + consumedEvents++; + } + FAIL() << "Got " << consumedEvents << "events, but still no event with seq=" << seq; +} + +/** + * All entries are compared against a single MotionEvent, but the same data structure + * InputEventEntry is used here for simpler code. As a result, the entire array of InputEventEntry + * must contain identical values for the action field. + */ +void TouchResamplingTest::consumeInputEventEntries(const std::vector<InputEventEntry>& entries, + std::chrono::nanoseconds frameTime) { + ASSERT_GE(entries.size(), 1U) << "Must have at least 1 InputEventEntry to compare against"; + + uint32_t consumeSeq; + InputEvent* event; + + status_t status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, frameTime.count(), + &consumeSeq, &event); + ASSERT_EQ(OK, status); + MotionEvent* motionEvent = static_cast<MotionEvent*>(event); + + ASSERT_EQ(entries.size() - 1, motionEvent->getHistorySize()); + for (size_t i = 0; i < entries.size(); i++) { // most recent sample is last + SCOPED_TRACE(i); + const InputEventEntry& entry = entries[i]; + ASSERT_EQ(entry.action, motionEvent->getAction()); + ASSERT_EQ(entry.eventTime.count(), motionEvent->getHistoricalEventTime(i)); + ASSERT_EQ(entry.pointers.size(), motionEvent->getPointerCount()); + + for (size_t p = 0; p < motionEvent->getPointerCount(); p++) { + SCOPED_TRACE(p); + // The pointers can be in any order, both in MotionEvent as well as InputEventEntry + ssize_t motionEventPointerIndex = motionEvent->findPointerIndex(entry.pointers[p].id); + ASSERT_GE(motionEventPointerIndex, 0) << "Pointer must be present in MotionEvent"; + ASSERT_EQ(entry.pointers[p].x, + motionEvent->getHistoricalAxisValue(AMOTION_EVENT_AXIS_X, + motionEventPointerIndex, i)); + ASSERT_EQ(entry.pointers[p].x, + motionEvent->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_X, + motionEventPointerIndex, i)); + ASSERT_EQ(entry.pointers[p].y, + motionEvent->getHistoricalAxisValue(AMOTION_EVENT_AXIS_Y, + motionEventPointerIndex, i)); + ASSERT_EQ(entry.pointers[p].y, + motionEvent->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, + motionEventPointerIndex, i)); + } + } + + status = mConsumer->sendFinishedSignal(consumeSeq, true); + ASSERT_EQ(OK, status); + + receiveResponseUntilSequence(consumeSeq); +} + +/** + * Timeline + * ---------+------------------+------------------+--------+-----------------+---------------------- + * 0 ms 10 ms 20 ms 25 ms 35 ms + * ACTION_DOWN ACTION_MOVE ACTION_MOVE ^ ^ + * | | + * resampled value | + * frameTime + * Typically, the prediction is made for time frameTime - RESAMPLE_LATENCY, or 30 ms in this case + * However, that would be 10 ms later than the last real sample (which came in at 20 ms). + * Therefore, the resampling should happen at 20 ms + RESAMPLE_MAX_PREDICTION = 28 ms. + * In this situation, though, resample time is further limited by taking half of the difference + * between the last two real events, which would put this time at: + * 20 ms + (20 ms - 10 ms) / 2 = 25 ms. + */ +TEST_F(TouchResamplingTest, EventIsResampled) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +/** + * Same as above test, but use pointer id=1 instead of 0 to make sure that system does not + * have these hardcoded. + */ +TEST_F(TouchResamplingTest, EventIsResampledWithDifferentId) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{1, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{1, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{1, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{1, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{1, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{1, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {25ms, {{1, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +/** + * Event should not be resampled when sample time is equal to event time. + */ +TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + // no resampled event because the time of resample falls exactly on the existing event + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +/** + * Once we send a resampled value to the app, we should continue to "lie" if the pointer + * does not move. So, if the pointer keeps the same coordinates, resampled value should continue + * to be used. + */ +TEST_F(TouchResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again, + // the system should still report 35. + entries = { + // id x y + {40ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/; + expectedEntries = { + // id x y + {40ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + {45ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +TEST_F(TouchResamplingTest, OldEventReceivedAfterResampleOccurs) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, + {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); + // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY + // because we are further bound by how far we can extrapolate by the "last time delta". + // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future + // from the event at 20ms, which is why the resampled event is at t = 25 ms. + + // We resampled the event to 25 ms. Now, an older 'real' event comes in. + entries = { + // id x y + {24ms, {{0, 40, 30}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 50ms; + expectedEntries = { + // id x y + {24ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + {26ms, {{0, 45, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // full action for when a pointer with id=1 appears (some other pointer must already be present) + constexpr int32_t actionPointer1Down = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + // full action for when a pointer with id=0 disappears (some other pointer must still remain) + constexpr int32_t actionPointer0Up = + AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + entries = { + // id x y + {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 10ms + 5ms /*RESAMPLE_LATENCY*/; + expectedEntries = { + // id x y + {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE}, + // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Second pointer id=1 appears + entries = { + // id x y + {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down}, + }; + publishInputEventEntries(entries); + frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/; + expectedEntries = { + // id x y + {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down}, + // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Both pointers move + entries = { + // id x y + {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE}, + {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/; + expectedEntries = { + // id x y + {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE}, + {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, + {45ms, {{0, 130, 130}, {1, 650, 650}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Both pointers move again + entries = { + // id x y + {60ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, + {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 75ms + 5ms /*RESAMPLE_LATENCY*/; + /** + * The sample at t = 60, pointer id 0 is not equal to 120, because this value of 120 was + * received twice, and resampled to 130. So if we already reported it as "130", we continue + * to report it as such. Similar with pointer id 1. + */ + expectedEntries = { + {60ms, + {{0, 130, 130}, // not 120! because it matches previous real event + {1, 650, 650}}, + AMOTION_EVENT_ACTION_MOVE}, + {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE}, + {75ms, {{0, 135, 135}, {1, 750, 750}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // First pointer id=0 leaves the screen + entries = { + // id x y + {80ms, {{1, 600, 600}}, actionPointer0Up}, + }; + publishInputEventEntries(entries); + frameTime = 90ms; + expectedEntries = { + // id x y + {80ms, {{1, 600, 600}}, actionPointer0Up}, + // no resampled event for ACTION_POINTER_UP + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Remaining pointer id=1 is still present, but doesn't move + entries = { + // id x y + {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 100ms; + expectedEntries = { + // id x y + {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, + /** + * The latest event with ACTION_MOVE was at t = 70, coord = 700. + * Use that value for resampling here: (600 - 700) / (90 - 70) * 5 + 600 + */ + {95ms, {{1, 575, 575}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +} // namespace android diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp index 654b236bda..081a995a6f 100644 --- a/libs/input/tests/TouchVideoFrame_test.cpp +++ b/libs/input/tests/TouchVideoFrame_test.cpp @@ -73,38 +73,38 @@ TEST(TouchVideoFrame, Equality) { TEST(TouchVideoFrame, Rotate90_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); - frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); - frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(ui::ROTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_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_90); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_90); + frame.rotate(ui::ROTATION_90); + frame.rotate(ui::ROTATION_90); + frame.rotate(ui::ROTATION_90); ASSERT_EQ(frame, frameOriginal); } @@ -113,43 +113,43 @@ TEST(TouchVideoFrame, Rotate90_3x2_4times) { TEST(TouchVideoFrame, Rotate180_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); - frame.rotate(DISPLAY_ORIENTATION_180); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_180); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_180); ASSERT_EQ(frame, frameRotated); } @@ -158,38 +158,38 @@ TEST(TouchVideoFrame, Rotate180_3x3) { TEST(TouchVideoFrame, Rotate270_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); - frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); - frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(ui::ROTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_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_270); + frame.rotate(ui::ROTATION_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); + frame.rotate(ui::ROTATION_270); + frame.rotate(ui::ROTATION_270); + frame.rotate(ui::ROTATION_270); + frame.rotate(ui::ROTATION_270); ASSERT_EQ(frame, frameOriginal); } diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index a87b1873f0..54feea2644 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -16,9 +16,10 @@ #define LOG_TAG "VelocityTracker_test" +#include <math.h> #include <array> #include <chrono> -#include <math.h> +#include <limits> #include <android-base/stringprintf.h> #include <attestation/HmacKeyManager.h> @@ -26,7 +27,9 @@ #include <gui/constants.h> #include <input/VelocityTracker.h> -using namespace std::chrono_literals; +using std::literals::chrono_literals::operator""ms; +using std::literals::chrono_literals::operator""ns; +using std::literals::chrono_literals::operator""us; using android::base::StringPrintf; namespace android { @@ -62,8 +65,15 @@ static void EXPECT_NEAR_BY_FRACTION(float actual, float target, float fraction) EXPECT_NEAR(actual, target, tolerance); } -static void checkVelocity(float Vactual, float Vtarget) { - EXPECT_NEAR_BY_FRACTION(Vactual, Vtarget, VELOCITY_TOLERANCE); +static void checkVelocity(std::optional<float> Vactual, std::optional<float> Vtarget) { + if (Vactual != std::nullopt) { + if (Vtarget == std::nullopt) { + FAIL() << "Expected no velocity, but found " << *Vactual; + } + EXPECT_NEAR_BY_FRACTION(*Vactual, *Vtarget, VELOCITY_TOLERANCE); + } else if (Vtarget != std::nullopt) { + FAIL() << "Expected velocity, but found no velocity"; + } } static void checkCoefficient(float actual, float target) { @@ -84,7 +94,7 @@ struct Position { } }; -struct MotionEventEntry { +struct PlanarMotionEventEntry { std::chrono::nanoseconds eventTime; std::vector<Position> positions; }; @@ -133,15 +143,43 @@ static int32_t resolveAction(const std::vector<Position>& lastPositions, return AMOTION_EVENT_ACTION_MOVE; } -static std::vector<MotionEvent> createMotionEventStream( - const std::vector<MotionEventEntry>& motions) { +static std::vector<MotionEvent> createAxisScrollMotionEventStream( + const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions) { + std::vector<MotionEvent> events; + for (const auto& [timeStamp, value] : motions) { + EXPECT_TRUE(!isnan(value)) << "The entry at pointerId must be valid"; + + PointerCoords coords[1]; + coords[0].setAxisValue(AMOTION_EVENT_AXIS_SCROLL, value); + + PointerProperties properties[1]; + properties[0].id = DEFAULT_POINTER_ID; + + MotionEvent event; + ui::Transform identityTransform; + event.initialize(InputEvent::nextId(), 5 /*deviceId*/, AINPUT_SOURCE_ROTARY_ENCODER, + ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL, + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, identityTransform, + 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/, + timeStamp.count(), 1 /*pointerCount*/, properties, coords); + + events.emplace_back(event); + } + + return events; +} + +static std::vector<MotionEvent> createTouchMotionEventStream( + const std::vector<PlanarMotionEventEntry>& motions) { if (motions.empty()) { ADD_FAILURE() << "Need at least 1 sample to create a MotionEvent. Received empty vector."; } std::vector<MotionEvent> events; for (size_t i = 0; i < motions.size(); i++) { - const MotionEventEntry& entry = motions[i]; + const PlanarMotionEventEntry& entry = motions[i]; BitSet32 pointers = getValidPointers(entry.positions); const uint32_t pointerCount = pointers.count(); @@ -149,12 +187,11 @@ static std::vector<MotionEvent> createMotionEventStream( if (i == 0) { action = AMOTION_EVENT_ACTION_DOWN; EXPECT_EQ(1U, pointerCount) << "First event should only have 1 pointer"; - } else if (i == motions.size() - 1) { - EXPECT_EQ(1U, pointerCount) << "Last event should only have 1 pointer"; + } else if ((i == motions.size() - 1) && pointerCount == 1) { action = AMOTION_EVENT_ACTION_UP; } else { - const MotionEventEntry& previousEntry = motions[i-1]; - const MotionEventEntry& nextEntry = motions[i+1]; + const PlanarMotionEventEntry& previousEntry = motions[i-1]; + const PlanarMotionEventEntry& nextEntry = motions[i+1]; action = resolveAction(previousEntry.positions, entry.positions, nextEntry.positions); } @@ -193,54 +230,217 @@ static std::vector<MotionEvent> createMotionEventStream( return events; } -static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, - const std::vector<MotionEventEntry>& motions, int32_t axis, - float targetVelocity) { +static std::optional<float> computePlanarVelocity( + const VelocityTracker::Strategy strategy, + const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, + uint32_t pointerId = DEFAULT_POINTER_ID) { VelocityTracker vt(strategy); - float Vx, Vy; - std::vector<MotionEvent> events = createMotionEventStream(motions); + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } - vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy); + return vt.getVelocity(axis, pointerId); +} +static std::vector<MotionEvent> createMotionEventStream( + int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) { switch (axis) { - case AMOTION_EVENT_AXIS_X: - checkVelocity(Vx, targetVelocity); - break; - case AMOTION_EVENT_AXIS_Y: - checkVelocity(Vy, targetVelocity); - break; - default: - FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y"; + case AMOTION_EVENT_AXIS_SCROLL: + return createAxisScrollMotionEventStream(motion); + default: + ADD_FAILURE() << "Axis " << axis << " is not supported"; + return {}; + } +} + +static std::optional<float> computeVelocity( + const VelocityTracker::Strategy strategy, + const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) { + VelocityTracker vt(strategy); + + for (const MotionEvent& event : createMotionEventStream(axis, motions)) { + vt.addMovement(&event); } + + return vt.getVelocity(axis, DEFAULT_POINTER_ID); +} + +static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, + const std::vector<PlanarMotionEventEntry>& motions, + int32_t axis, std::optional<float> targetVelocity, + uint32_t pointerId = DEFAULT_POINTER_ID) { + checkVelocity(computePlanarVelocity(strategy, motions, axis, pointerId), targetVelocity); +} + +static void computeAndCheckAxisScrollVelocity( + const VelocityTracker::Strategy strategy, + const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, + std::optional<float> targetVelocity) { + checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); } -static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, - const std::array<float, 3>& coefficients) { +static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions, + const std::array<float, 3>& coefficients) { VelocityTracker vt(VelocityTracker::Strategy::LSQ2); - std::vector<MotionEvent> events = createMotionEventStream(motions); + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } - VelocityTracker::Estimator estimator; - EXPECT_TRUE(vt.getEstimator(0, &estimator)); + VelocityTracker::Estimator estimatorX; + VelocityTracker::Estimator estimatorY; + EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX)); + EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY)); for (size_t i = 0; i< coefficients.size(); i++) { - checkCoefficient(estimator.xCoeff[i], coefficients[i]); - checkCoefficient(estimator.yCoeff[i], coefficients[i]); + checkCoefficient(estimatorX.coeff[i], coefficients[i]); + checkCoefficient(estimatorY.coeff[i], coefficients[i]); + } +} + +/* + *================== VelocityTracker tests that do not require test motion data ==================== + */ +TEST(SimpleVelocityTrackerTest, TestSupportedAxis) { + // Note that we are testing up to the max possible axis value, plus 3 more values. We are going + // beyond the max value to add a bit more protection. "3" is chosen arbitrarily to cover a few + // more values beyond the max. + for (int32_t axis = 0; axis <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE + 3; axis++) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_SCROLL: + EXPECT_TRUE(VelocityTracker::isAxisSupported(axis)) << axis << " is supported"; + break; + default: + EXPECT_FALSE(VelocityTracker::isAxisSupported(axis)) << axis << " is NOT supported"; + } } } /* * ================== VelocityTracker tests generated manually ===================================== */ +TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) { + std::vector<PlanarMotionEventEntry> motions = {{10ms, {{2, 4}}}, + {20ms, {{4, 12}}}, + {30ms, {{6, 20}}}, + {40ms, {{10, 30}}}}; + + EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X), + computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, + AMOTION_EVENT_AXIS_X)); + EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y), + computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, + AMOTION_EVENT_AXIS_Y)); +} + +TEST_F(VelocityTrackerTest, TestComputedVelocity) { + VelocityTracker::ComputedVelocity computedVelocity; + + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/); + + // Check the axes/indices with velocity. + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000); + for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) { + // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, id)) + << "Empty scroll data expected at id=" << id; + if (id == 0 || id == 26U || id == 27U || id == MAX_POINTER_ID) { + // Already checked above; continue. + continue; + } + // No data was added to X/Y for this id, expect empty value. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id)) + << "Empty X data expected at id=" << id; + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id)) + << "Empty Y data expected at id=" << id; + } + // Out-of-bounds ids should given empty values. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, -1)); + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1)); +} + +TEST_F(VelocityTrackerTest, TestGetComputedVelocity) { + std::vector<PlanarMotionEventEntry> motions = { + {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}}, + {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}}, + {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}}, + {235089112497111ns, {{528.25, 0}}}, {235089118760000ns, {{531.00, 0}}}, + {235089126686000ns, {{535.00, 0}}}, {235089129316820ns, {{536.33, 0}}}, + {235089135199000ns, {{540.00, 0}}}, {235089144297000ns, {{546.00, 0}}}, + {235089146136443ns, {{547.21, 0}}}, {235089152923000ns, {{553.00, 0}}}, + {235089160784000ns, {{559.00, 0}}}, {235089162955851ns, {{560.66, 0}}}, + {235089162955851ns, {{560.66, 0}}}, // ACTION_UP + }; + VelocityTracker vt(VelocityTracker::Strategy::IMPULSE); + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); + for (const MotionEvent& event : events) { + vt.addMovement(&event); + } + + float maxFloat = std::numeric_limits<float>::max(); + VelocityTracker::ComputedVelocity computedVelocity; + computedVelocity = vt.getComputedVelocity(1000 /* units */, maxFloat); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), + 764.345703); + + // Expect X velocity to be scaled with respective to provided units. + computedVelocity = vt.getComputedVelocity(1000000 /* units */, maxFloat); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), + 764345.703); + + // Expect X velocity to be clamped by provided max velocity. + computedVelocity = vt.getComputedVelocity(1000000 /* units */, 1000); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000); + + // All 0 data for Y; expect 0 velocity. + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)), 0); + + // No data for scroll-axis; expect empty velocity. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID)); +} + +TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { + VelocityTracker vt(VelocityTracker::Strategy::DEFAULT); + + EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); + + VelocityTracker::Estimator estimator; + EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID, &estimator)); + + VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000); + for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) { + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id)); + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id)); + } + + EXPECT_EQ(-1, vt.getActivePointerId()); + + // Make sure that the clearing functions execute without an issue. + vt.clearPointers(BitSet32(7U)); + vt.clear(); +} + TEST_F(VelocityTrackerTest, 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. - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { {0ms, {{273, 0}}}, {12585us, {{293, 0}}}, {14730us, {{293, 0}}}, @@ -252,7 +452,7 @@ TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { // Same coordinate is reported 3 times in a row - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { {0ms, {{293, 0}}}, {6132us, {{293, 0}}}, {11283us, {{293, 0}}}, @@ -264,7 +464,7 @@ TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // Fixed velocity at 5 points per 10 milliseconds - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP }; computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500); @@ -288,7 +488,7 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // --------------- Recorded by hand on swordfish --------------------------------------------------- TEST_F(VelocityTrackerTest, SwordfishFlingDown) { // Recording of a fling on Swordfish that could cause a fling in the wrong direction - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{271, 96}} }, { 16071042ns, {{269.786346, 106.922775}} }, { 35648403ns, {{267.983063, 156.660034}} }, @@ -323,7 +523,7 @@ TEST_F(VelocityTrackerTest, SwordfishFlingDown) { TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { // Sailfish - fling up - slow - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235089067457000ns, {{528.00, 983.00}} }, { 235089084684000ns, {{527.00, 981.00}} }, { 235089093349000ns, {{527.00, 977.00}} }, @@ -355,7 +555,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { // Sailfish - fling up - slow - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235110560704000ns, {{522.00, 1107.00}} }, { 235110575764000ns, {{522.00, 1107.00}} }, { 235110584385000ns, {{522.00, 1107.00}} }, @@ -384,7 +584,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { // Sailfish - fling up - slow - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 792536237000ns, {{580.00, 1317.00}} }, { 792541538987ns, {{580.63, 1311.94}} }, { 792544613000ns, {{581.00, 1309.00}} }, @@ -418,7 +618,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { // Sailfish - fling up - faster - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235160420675000ns, {{610.00, 1042.00}} }, { 235160428220000ns, {{609.00, 1026.00}} }, { 235160436544000ns, {{609.00, 1024.00}} }, @@ -452,7 +652,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { // Sailfish - fling up - faster - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 847153808000ns, {{576.00, 1264.00}} }, { 847171174000ns, {{576.00, 1262.00}} }, { 847179640000ns, {{576.00, 1257.00}} }, @@ -478,7 +678,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { // Sailfish - fling up - faster - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235200532789000ns, {{507.00, 1084.00}} }, { 235200549221000ns, {{507.00, 1083.00}} }, { 235200557841000ns, {{507.00, 1081.00}} }, @@ -504,7 +704,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { // Sailfish - fling up - fast - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 920922149000ns, {{561.00, 1412.00}} }, { 920930185000ns, {{559.00, 1377.00}} }, { 920930262463ns, {{558.98, 1376.66}} }, @@ -533,7 +733,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { // Sailfish - fling up - fast - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235247153233000ns, {{518.00, 1168.00}} }, { 235247170452000ns, {{517.00, 1167.00}} }, { 235247178908000ns, {{515.00, 1159.00}} }, @@ -556,7 +756,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { // Sailfish - fling up - fast - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235302568736000ns, {{529.00, 1167.00}} }, { 235302576644000ns, {{523.00, 1140.00}} }, { 235302579395063ns, {{520.91, 1130.61}} }, @@ -577,7 +777,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { // Sailfish - fling down - slow - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235655749552755ns, {{582.00, 432.49}} }, { 235655750638000ns, {{582.00, 433.00}} }, { 235655758865000ns, {{582.00, 440.00}} }, @@ -611,7 +811,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { // Sailfish - fling down - slow - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235671152083370ns, {{485.24, 558.28}} }, { 235671154126000ns, {{485.00, 559.00}} }, { 235671162497000ns, {{484.00, 566.00}} }, @@ -645,7 +845,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { // Sailfish - fling down - slow - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 170983201000ns, {{557.00, 533.00}} }, { 171000668000ns, {{556.00, 534.00}} }, { 171007359750ns, {{554.73, 535.27}} }, @@ -672,7 +872,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { // Sailfish - fling down - faster - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235695280333000ns, {{558.00, 451.00}} }, { 235695283971237ns, {{558.43, 454.45}} }, { 235695289038000ns, {{559.00, 462.00}} }, @@ -702,7 +902,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { // Sailfish - fling down - faster - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235709624766000ns, {{535.00, 579.00}} }, { 235709642256000ns, {{534.00, 580.00}} }, { 235709643350278ns, {{533.94, 580.06}} }, @@ -733,7 +933,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { // Sailfish - fling down - faster - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235727628927000ns, {{540.00, 440.00}} }, { 235727636810000ns, {{537.00, 454.00}} }, { 235727646176000ns, {{536.00, 454.00}} }, @@ -762,7 +962,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { // Sailfish - fling down - fast - 1 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235762352849000ns, {{467.00, 286.00}} }, { 235762360250000ns, {{443.00, 344.00}} }, { 235762362787412ns, {{434.77, 363.89}} }, @@ -783,7 +983,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { // Sailfish - fling down - fast - 2 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 235772487188000ns, {{576.00, 204.00}} }, { 235772495159000ns, {{553.00, 236.00}} }, { 235772503568000ns, {{551.00, 240.00}} }, @@ -804,7 +1004,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { // Sailfish - fling down - fast - 3 - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 507650295000ns, {{628.00, 233.00}} }, { 507658234000ns, {{605.00, 269.00}} }, { 507666784000ns, {{601.00, 274.00}} }, @@ -830,12 +1030,12 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { /** * ================== Multiple pointers ============================================================ * - * Three fingers quickly tap the screen. Since this is a tap, the velocities should be zero. + * Three fingers quickly tap the screen. Since this is a tap, the velocities should be empty. * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be * part of the fitted data), this can cause large velocity values to be reported instead. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, { 10800us, {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN { 10800us, {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_DOWN @@ -844,12 +1044,78 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFi { 272700us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, }; - // Velocity should actually be zero, but we expect 0.016 here instead. - // This is close enough to zero, and is likely caused by division by a very small number. - computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016); - computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016); - computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + std::nullopt); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + std::nullopt); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + std::nullopt); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + std::nullopt); +} + +/** + * ================= Pointer liftoff =============================================================== + */ + +/** + * The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a short delay + * between the last ACTION_MOVE and the next ACTION_POINTER_UP or ACTION_UP, velocity should not be + * affected by the liftoff. + */ +TEST_F(VelocityTrackerTest, ShortDelayBeforeActionUp) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{10, 0}}}, {10ms, {{20, 0}}}, {20ms, {{30, 0}}}, {30ms, {{30, 0}}}, // ACTION_UP + }; + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1000); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 1000); +} + +/** + * The last movement of a single pointer is ACTION_UP. If there's a long delay between the last + * ACTION_MOVE and the final ACTION_UP, velocity should be reported as empty because the pointer + * should be assumed to have stopped. + */ +TEST_F(VelocityTrackerTest, LongDelayBeforeActionUp) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{10, 0}}}, + {10ms, {{20, 0}}}, + {20ms, {{30, 0}}}, + {3000ms, {{30, 0}}}, // ACTION_UP + }; + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + std::nullopt); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + std::nullopt); +} + +/** + * The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a long delay + * before ACTION_POINTER_UP event, the movement should be assumed to have stopped. + * The final velocity should be reported as empty for all pointers. + */ +TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{10, 0}}}, + {10ms, {{20, 0}, {100, 0}}}, + {20ms, {{30, 0}, {200, 0}}}, + {30ms, {{30, 0}, {300, 0}}}, + {40ms, {{30, 0}, {400, 0}}}, + {3000ms, {{30, 0}}}, // ACTION_POINTER_UP + }; + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + std::nullopt, + /*pointerId*/ 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + std::nullopt, + /*pointerId*/ 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + std::nullopt, + /*pointerId*/ 1); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + std::nullopt, + /*pointerId*/ 1); } /** @@ -878,7 +1144,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFi * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2). */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, // 0 s { 1ms, {{1, 1}} }, // 0.001 s { 2ms, {{1, 1}} }, // 0.002 s @@ -896,7 +1162,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constan * Straight line y = x :: the constant and quadratic coefficients are zero. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{-2, -2}} }, { 1ms, {{-1, -1}} }, { 2ms, {{-0, -0}} }, @@ -914,7 +1180,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) * Parabola */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, { 2ms, {{8, 8}} }, @@ -932,7 +1198,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol * Parabola */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, { 2ms, {{9, 9}} }, @@ -950,7 +1216,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol * Parabola :: y = x^2 :: the constant and linear coefficients are zero. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) { - std::vector<MotionEventEntry> motions = { + std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{4, 4}} }, { 1ms, {{1, 1}} }, { 2ms, {{0, 0}} }, @@ -964,4 +1230,114 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6})); } +// Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity. +TEST_F(VelocityTrackerTest, AxisScrollVelocity) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = { + {235089067457000ns, 0.00}, {235089084684000ns, -1.00}, {235089093349000ns, 0.00}, + {235089095677625ns, 0.00}, {235089101859000ns, 0.00}, {235089110378000ns, 0.00}, + {235089112497111ns, 0.25}, {235089118760000ns, 1.75}, {235089126686000ns, 4.00}, + {235089129316820ns, 1.33}, {235089135199000ns, 3.67}, {235089144297000ns, 6.00}, + {235089146136443ns, 1.21}, {235089152923000ns, 5.79}, {235089160784000ns, 6.00}, + {235089162955851ns, 1.66}, + }; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {764.345703}); +} + +// --------------- Recorded by hand on a Wear OS device using a rotating side button --------------- +TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = { + {224598065152ns, -0.050100}, {224621871104ns, -0.133600}, {224645464064ns, -0.551100}, + {224669171712ns, -0.801600}, {224687063040ns, -1.035400}, {224706691072ns, -0.484300}, + {224738213888ns, -0.334000}, {224754401280ns, -0.083500}, + }; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-27.86}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollUp) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = { + {269606010880ns, 0.050100}, {269626064896ns, 0.217100}, {269641973760ns, 0.267200}, + {269658079232ns, 0.267200}, {269674217472ns, 0.267200}, {269690683392ns, 0.367400}, + {269706133504ns, 0.551100}, {269722173440ns, 0.501000}, + }; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {31.92}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown_ThenUp_ThenDown) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = { + {2580534001664ns, -0.033400}, {2580549992448ns, -0.133600}, + {2580566769664ns, -0.250500}, {2580581974016ns, -0.183700}, + {2580597964800ns, -0.267200}, {2580613955584ns, -0.551100}, + {2580635189248ns, -0.601200}, {2580661927936ns, -0.450900}, + {2580683161600ns, -0.417500}, {2580705705984ns, -0.150300}, + {2580722745344ns, -0.016700}, {2580786446336ns, 0.050100}, + {2580801912832ns, 0.150300}, {2580822360064ns, 0.300600}, + {2580838088704ns, 0.300600}, {2580854341632ns, 0.400800}, + {2580869808128ns, 0.517700}, {2580886061056ns, 0.501000}, + {2580905984000ns, 0.350700}, {2580921974784ns, 0.350700}, + {2580937965568ns, 0.066800}, {2580974665728ns, 0.016700}, + {2581034434560ns, -0.066800}, {2581049901056ns, -0.116900}, + {2581070610432ns, -0.317300}, {2581086076928ns, -0.200400}, + {2581101805568ns, -0.233800}, {2581118058496ns, -0.417500}, + {2581134049280ns, -0.417500}, {2581150040064ns, -0.367400}, + {2581166030848ns, -0.267200}, {2581181759488ns, -0.150300}, + {2581199847424ns, -0.066800}, + }; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-9.73}); +} + +// ------------------------------- Hand generated test cases --------------------------------------- +TEST_F(VelocityTrackerTest, TestDefaultStrategyForAxisScroll) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = { + {10ms, 20}, + {20ms, 25}, + {30ms, 50}, + {40ms, 100}, + }; + + EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions, + AMOTION_EVENT_AXIS_SCROLL), + computeVelocity(VelocityTracker::Strategy::DEFAULT, motions, + AMOTION_EVENT_AXIS_SCROLL)); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_SimilarDifferentialValues) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 2.12}, {3ns, 2.12}, + {7ns, 2.12}, {8ns, 2.12}, + {15ns, 2.12}, {18ns, 2.12}}; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {1690236059.86}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_OnlyTwoValues) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 5}, {2ms, 10}}; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {10000}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_ConstantVelocity) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 20}, {2ms, 20}, + {3ms, 20}, {4ms, 20}, + {5ms, 20}, {6ms, 20}}; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {20000}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoMotion) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 0}, {2ns, 0}, + {3ns, 0}, {4ns, 0}, + {5ns, 0}, {6ns, 0}}; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {0}); +} + +TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoData) { + std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {}; + + computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, std::nullopt); +} + } // namespace android diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp new file mode 100644 index 0000000000..54af7c912d --- /dev/null +++ b/libs/jpegrecoverymap/Android.bp @@ -0,0 +1,70 @@ +// Copyright 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_static { + name: "libjpegrecoverymap", + host_supported: true, + + export_include_dirs: ["include"], + local_include_dirs: ["include"], + + srcs: [ + "recoverymap.cpp", + "recoverymapmath.cpp", + "recoverymaputils.cpp", + ], + + shared_libs: [ + "libimage_io", + "libjpeg", + "libutils", + ], +} + +cc_library_static { + name: "libjpegencoder", + + shared_libs: [ + "libjpeg", + ], + + export_include_dirs: ["include"], + + srcs: [ + "jpegencoder.cpp", + ], +} + +cc_library_static { + name: "libjpegdecoder", + + shared_libs: [ + "libjpeg", + ], + + export_include_dirs: ["include"], + + srcs: [ + "jpegdecoder.cpp", + ], +} diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS new file mode 100644 index 0000000000..133af5bcd4 --- /dev/null +++ b/libs/jpegrecoverymap/OWNERS @@ -0,0 +1,4 @@ +arifdikici@google.com +deakin@google.com +dichenzhang@google.com +kyslov@google.com
\ No newline at end of file diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h new file mode 100644 index 0000000000..5c9c8b6ec6 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h @@ -0,0 +1,99 @@ + +/* + * Copyright 2022 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_JPEGRECOVERYMAP_JPEGDECODER_H +#define ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H + +// We must include cstdio before jpeglib.h. It is a requirement of libjpeg. +#include <cstdio> +extern "C" { +#include <jerror.h> +#include <jpeglib.h> +} +#include <utils/Errors.h> +#include <vector> +namespace android::recoverymap { +/* + * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format. + * This class is not thread-safe. + */ +class JpegDecoder { +public: + JpegDecoder(); + ~JpegDecoder(); + /* + * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling + * this method, call getDecompressedImage() to get the image. + * Returns false if decompressing the image fails. + */ + bool decompressImage(const void* image, int length); + /* + * Returns the decompressed raw image buffer pointer. This method must be called only after + * calling decompressImage(). + */ + void* getDecompressedImagePtr(); + /* + * Returns the decompressed raw image buffer size. This method must be called only after + * calling decompressImage(). + */ + size_t getDecompressedImageSize(); + /* + * Returns the image width in pixels. This method must be called only after calling + * decompressImage(). + */ + size_t getDecompressedImageWidth(); + /* + * Returns the image width in pixels. This method must be called only after calling + * decompressImage(). + */ + size_t getDecompressedImageHeight(); + /* + * Returns the XMP data from the image. + */ + void* getXMPPtr(); + /* + * Returns the decompressed XMP buffer size. This method must be called only after + * calling decompressImage(). + */ + size_t getXMPSize(); + + bool getCompressedImageParameters(const void* image, int length, + size_t* pWidth, size_t* pHeight, + std::vector<uint8_t>* &iccData, + std::vector<uint8_t>* &exifData); + +private: + bool decode(const void* image, int length); + // Returns false if errors occur. + bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel); + bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest); + bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest); + // Process 16 lines of Y and 16 lines of U/V each time. + // We must pass at least 16 scanlines according to libjpeg documentation. + static const int kCompressBatchSize = 16; + // The buffer that holds the decompressed result. + std::vector<JOCTET> mResultBuffer; + // The buffer that holds XMP Data. + std::vector<JOCTET> mXMPBuffer; + + // Resolution of the decompressed image. + size_t mWidth; + size_t mHeight; +}; +} /* namespace android */ + +#endif // ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h new file mode 100644 index 0000000000..61aeb8ace7 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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_JPEGRECOVERYMAP_JPEGENCODER_H +#define ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H + +// We must include cstdio before jpeglib.h. It is a requirement of libjpeg. +#include <cstdio> + +extern "C" { +#include <jerror.h> +#include <jpeglib.h> +} + +#include <utils/Errors.h> +#include <vector> + +namespace android::recoverymap { + +/* + * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format. + * This class is not thread-safe. + */ +class JpegEncoder { +public: + JpegEncoder(); + ~JpegEncoder(); + + /* + * Compresses YUV420Planer image to JPEG format. After calling this method, call + * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use. + * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of + * ICC segment which will be added to the compressed image. + * Returns false if errors occur during compression. + */ + bool compressImage(const void* image, int width, int height, int quality, + const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false); + + /* + * Returns the compressed JPEG buffer pointer. This method must be called only after calling + * compressImage(). + */ + void* getCompressedImagePtr(); + + /* + * Returns the compressed JPEG buffer size. This method must be called only after calling + * compressImage(). + */ + size_t getCompressedImageSize(); + +private: + // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be + // passed into jpeg library. + static void initDestination(j_compress_ptr cinfo); + static boolean emptyOutputBuffer(j_compress_ptr cinfo); + static void terminateDestination(j_compress_ptr cinfo); + static void outputErrorMessage(j_common_ptr cinfo); + + // Returns false if errors occur. + bool encode(const void* inYuv, int width, int height, int jpegQuality, + const void* iccBuffer, unsigned int iccSize, bool isSingleChannel); + void setJpegDestination(jpeg_compress_struct* cinfo); + void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo, + bool isSingleChannel); + // Returns false if errors occur. + bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel); + bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv); + bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image); + + // The block size for encoded jpeg image buffer. + static const int kBlockSize = 16384; + // Process 16 lines of Y and 16 lines of U/V each time. + // We must pass at least 16 scanlines according to libjpeg documentation. + static const int kCompressBatchSize = 16; + + // The buffer that holds the compressed result. + std::vector<JOCTET> mResultBuffer; +}; + +} /* namespace android */ + +#endif // ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h new file mode 100644 index 0000000000..6995762ea2 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/Errors.h> + +namespace android::recoverymap { + +enum { + // status_t map for errors in the media framework + // OK or NO_ERROR or 0 represents no error. + + // See system/core/include/utils/Errors.h + // System standard errors from -1 through (possibly) -133 + // + // Errors with special meanings and side effects. + // INVALID_OPERATION: Operation attempted in an illegal state (will try to signal to app). + // DEAD_OBJECT: Signal from CodecBase to MediaCodec that MediaServer has died. + // NAME_NOT_FOUND: Signal from CodecBase to MediaCodec that the component was not found. + + // JPEGR errors + JPEGR_IO_ERROR_BASE = -10000, + ERROR_JPEGR_INVALID_INPUT_TYPE = JPEGR_IO_ERROR_BASE, + ERROR_JPEGR_INVALID_OUTPUT_TYPE = JPEGR_IO_ERROR_BASE - 1, + ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2, + ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3, + ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4, + ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5, + + JPEGR_RUNTIME_ERROR_BASE = -20000, + ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1, + ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2, + ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3, + ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4, + ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5, +}; + +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h new file mode 100644 index 0000000000..74f9776be6 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -0,0 +1,400 @@ +/* + * Copyright 2022 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_JPEGRECOVERYMAP_RECOVERYMAP_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H + +#include "jpegrerrorcode.h" + +namespace android::recoverymap { + +typedef enum { + JPEGR_COLORGAMUT_UNSPECIFIED, + JPEGR_COLORGAMUT_BT709, + JPEGR_COLORGAMUT_P3, + JPEGR_COLORGAMUT_BT2100, +} jpegr_color_gamut; + +// Transfer functions as defined for XMP metadata +typedef enum { + JPEGR_TF_HLG = 0, + JPEGR_TF_PQ = 1, +} jpegr_transfer_function; + +struct jpegr_info_struct { + size_t width; + size_t height; + std::vector<uint8_t>* iccData; + std::vector<uint8_t>* exifData; +}; + +/* + * Holds information for uncompressed image or recovery map. + */ +struct jpegr_uncompressed_struct { + // Pointer to the data location. + void* data; + // Width of the recovery map or image in pixels. + int width; + // Height of the recovery map or image in pixels. + int height; + // Color gamut. + jpegr_color_gamut colorGamut; +}; + +/* + * Holds information for compressed image or recovery map. + */ +struct jpegr_compressed_struct { + // Pointer to the data location. + void* data; + // Used data length in bytes. + int length; + // Maximum available data length in bytes. + int maxLength; + // Color gamut. + jpegr_color_gamut colorGamut; +}; + +/* + * Holds information for EXIF metadata. + */ +struct jpegr_exif_struct { + // Pointer to the data location. + void* data; + // Data length; + int length; +}; + +struct chromaticity_coord { + float x; + float y; +}; + + +struct st2086_metadata { + // xy chromaticity coordinate of the red primary of the mastering display + chromaticity_coord redPrimary; + // xy chromaticity coordinate of the green primary of the mastering display + chromaticity_coord greenPrimary; + // xy chromaticity coordinate of the blue primary of the mastering display + chromaticity_coord bluePrimary; + // xy chromaticity coordinate of the white point of the mastering display + chromaticity_coord whitePoint; + // Maximum luminance in nits of the mastering display + uint32_t maxLuminance; + // Minimum luminance in nits of the mastering display + float minLuminance; +}; + +struct hdr10_metadata { + // Mastering display color volume + st2086_metadata st2086Metadata; + // Max frame average light level in nits + float maxFALL; + // Max content light level in nits + float maxCLL; +}; + +struct jpegr_metadata { + // JPEG/R version + uint32_t version; + // Range scaling factor for the map + float rangeScalingFactor; + // The transfer function for decoding the HDR representation of the image + jpegr_transfer_function transferFunction; + // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ + hdr10_metadata hdr10Metadata; +}; + +typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr; +typedef struct jpegr_compressed_struct* jr_compressed_ptr; +typedef struct jpegr_exif_struct* jr_exif_ptr; +typedef struct jpegr_metadata* jr_metadata_ptr; +typedef struct jpegr_info_struct* jr_info_ptr; + +class RecoveryMap { +public: + /* + * Encode API-0 + * Compress JPEGR image from 10-bit HDR YUV. + * + * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images, + * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed + * JPEG. + * @param uncompressed_p010_image uncompressed HDR image in P010 color format + * @param hdr_tf transfer function of the HDR image + * @param dest destination of the compressed JPEGR image + * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is + * the highest quality + * @param exif pointer to the exif metadata. + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest, + int quality, + jr_exif_ptr exif); + + /* + * Encode API-1 + * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. + * + * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append + * the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same + * resolution. + * @param uncompressed_p010_image uncompressed HDR image in P010 color format + * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format + * @param hdr_tf transfer function of the HDR image + * @param dest destination of the compressed JPEGR image + * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is + * the highest quality + * @param exif pointer to the exif metadata. + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr uncompressed_yuv_420_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest, + int quality, + jr_exif_ptr exif); + + /* + * Encode API-2 + * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG. + * + * This method requires HAL Hardware JPEG encoder. + * + * Generate recovery map from the HDR and SDR inputs, append the recovery map to the end of the + * compressed JPEG. HDR and SDR inputs must be the same resolution and color space. + * @param uncompressed_p010_image uncompressed HDR image in P010 color format + * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format + * Note: the SDR image must be the decoded version of the JPEG + * input + * @param compressed_jpeg_image compressed 8-bit JPEG image + * @param hdr_tf transfer function of the HDR image + * @param dest destination of the compressed JPEGR image + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest); + + /* + * Encode API-3 + * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. + * + * This method requires HAL Hardware JPEG encoder. + * + * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input + * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR + * and SDR inputs must be the same resolution. + * @param uncompressed_p010_image uncompressed HDR image in P010 color format + * @param compressed_jpeg_image compressed 8-bit JPEG image + * @param hdr_tf transfer function of the HDR image + * @param dest destination of the compressed JPEGR image + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest); + + /* + * Decode API + * Decompress JPEGR image. + * + * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR. + * @param compressed_jpegr_image compressed JPEGR image + * @param dest destination of the uncompressed JPEGR image + * @param exif destination of the decoded EXIF metadata. + * @param request_sdr flag that request SDR output. If set to true, decoder will only decode + * the primary image which is SDR. Setting of request_sdr and input source + * (HDR or SDR) can be found in the table below: + * | input source | request_sdr | output of decoding | + * | HDR | true | SDR | + * | HDR | false | HDR | + * | SDR | true | SDR | + * | SDR | false | SDR | + * @return NO_ERROR if decoding succeeds, error code if error occurs. + */ + status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, + jr_uncompressed_ptr dest, + jr_exif_ptr exif = nullptr, + bool request_sdr = false); + + /* + * Gets Info from JPEGR file without decoding it. + * + * The output is filled jpegr_info structure + * @param compressed_jpegr_image compressed JPEGR image + * @param jpegr_info pointer to output JPEGR info + * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise + */ + status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, + jr_info_ptr jpegr_info); +private: + /* + * This method is called in the decoding pipeline. It will decode the recovery map. + * + * @param compressed_recovery_map compressed recovery map + * @param dest decoded recover map + * @return NO_ERROR if decoding succeeds, error code if error occurs. + */ + status_t decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map, + jr_uncompressed_ptr dest); + + /* + * This method is called in the encoding pipeline. It will encode the recovery map. + * + * @param uncompressed_recovery_map uncompressed recovery map + * @param dest encoded recover map + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, + jr_compressed_ptr dest); + + /* + * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and + * 10-bit yuv images as input, and calculate the uncompressed recovery map. The input images + * must be the same resolution. + * + * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format + * @param uncompressed_p010_image uncompressed HDR image in P010 color format + * @param dest recovery map; caller responsible for memory of data + * @param metadata metadata provides the transfer function for the HDR + * image; range_scaling_factor and hdr10 FALL and CLL will + * be updated. + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_uncompressed_ptr uncompressed_p010_image, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest); + + /* + * This method is called in the decoding pipeline. It will take the uncompressed (decoded) + * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as + * input, and calculate the 10-bit recovered image. The recovered output image is the same + * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata, + * and is in RGBA1010102 data format. + * + * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format + * @param uncompressed_recovery_map uncompressed recovery map + * @param metadata JPEG/R metadata extracted from XMP. + * @param dest reconstructed HDR image + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_uncompressed_ptr uncompressed_recovery_map, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest); + + /* + * This methoud is called to separate primary image and recovery map image from JPEGR + * + * @param compressed_jpegr_image compressed JPEGR image + * @param primary_image destination of primary image + * @param recovery_map destination of compressed recovery map + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr primary_image, + jr_compressed_ptr recovery_map); + /* + * This method is called in the decoding pipeline. It will read XMP metadata to find the start + * position of the compressed recovery map, and will extract the compressed recovery map. + * + * @param compressed_jpegr_image compressed JPEGR image + * @param dest destination of compressed recovery map + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr dest); + + /* + * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image + * and the compressed recovery map as input, and update the XMP metadata with the end of JPEG + * marker, and append the compressed gian map after the JPEG. + * + * @param compressed_jpeg_image compressed 8-bit JPEG image + * @param compress_recovery_map compressed recover map + * @param metadata JPEG/R metadata to encode in XMP of the jpeg + * @param dest compressed JPEGR image + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr compressed_recovery_map, + jr_metadata_ptr metadata, + jr_compressed_ptr dest); + + /* + * This method generates XMP metadata. + * + * below is an example of the XMP metadata that this function generates where + * secondary_image_length = 1000 + * range_scaling_factor = 1.25 + * + * <x:xmpmeta + * xmlns:x="adobe:ns:meta/" + * x:xmptk="Adobe XMP Core 5.1.2"> + * <rdf:RDF + * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + * <rdf:Description + * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"> + * <GContainer:Version>1</GContainer:Version> + * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor> + * <GContainer:Directory> + * <rdf:Seq> + * <rdf:li> + * <GContainer:Item + * Item:Semantic="Primary" + * Item:Mime="image/jpeg"/> + * </rdf:li> + * <rdf:li> + * <GContainer:Item + * Item:Semantic="RecoveryMap" + * Item:Mime="image/jpeg" + * Item:Length="1000"/> + * </rdf:li> + * </rdf:Seq> + * </GContainer:Directory> + * </rdf:Description> + * </rdf:RDF> + * </x:xmpmeta> + * + * @param secondary_image_length length of secondary image + * @param metadata JPEG/R metadata to encode as XMP + * @return XMP metadata in type of string + */ + std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); + + /* + * This method will tone map a HDR image to an SDR image. + * + * @param uncompressed_p010_image (input) uncompressed P010 image + * @param dest (output) tone mapping result as a YUV_420 image + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr dest); +}; + +} // namespace android::recoverymap + +#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h new file mode 100644 index 0000000000..0fb64d3c15 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -0,0 +1,296 @@ +/* + * Copyright 2022 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_JPEGRECOVERYMAP_RECOVERYMAPMATH_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H + +#include <stdint.h> + +#include <jpegrecoverymap/recoverymap.h> + +namespace android::recoverymap { + +//////////////////////////////////////////////////////////////////////////////// +// Framework + +const float kSdrWhiteNits = 100.0f; +const float kHlgMaxNits = 1000.0f; +const float kPqMaxNits = 10000.0f; + +struct Color { + union { + struct { + float r; + float g; + float b; + }; + struct { + float y; + float u; + float v; + }; + }; +}; + +typedef Color (*ColorTransformFn)(Color); +typedef float (*ColorCalculationFn)(Color); + +inline Color operator+=(Color& lhs, const Color& rhs) { + lhs.r += rhs.r; + lhs.g += rhs.g; + lhs.b += rhs.b; + return lhs; +} +inline Color operator-=(Color& lhs, const Color& rhs) { + lhs.r -= rhs.r; + lhs.g -= rhs.g; + lhs.b -= rhs.b; + return lhs; +} + +inline Color operator+(const Color& lhs, const Color& rhs) { + Color temp = lhs; + return temp += rhs; +} +inline Color operator-(const Color& lhs, const Color& rhs) { + Color temp = lhs; + return temp -= rhs; +} + +inline Color operator+=(Color& lhs, const float rhs) { + lhs.r += rhs; + lhs.g += rhs; + lhs.b += rhs; + return lhs; +} +inline Color operator-=(Color& lhs, const float rhs) { + lhs.r -= rhs; + lhs.g -= rhs; + lhs.b -= rhs; + return lhs; +} +inline Color operator*=(Color& lhs, const float rhs) { + lhs.r *= rhs; + lhs.g *= rhs; + lhs.b *= rhs; + return lhs; +} +inline Color operator/=(Color& lhs, const float rhs) { + lhs.r /= rhs; + lhs.g /= rhs; + lhs.b /= rhs; + return lhs; +} + +inline Color operator+(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp += rhs; +} +inline Color operator-(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp -= rhs; +} +inline Color operator*(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp *= rhs; +} +inline Color operator/(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp /= rhs; +} + + +//////////////////////////////////////////////////////////////////////////////// +// sRGB transformations +// NOTE: sRGB has the same color primaries as BT.709, but different transfer +// function. For this reason, all sRGB transformations here apply to BT.709, +// except for those concerning transfer functions. + +/* + * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1. + * + * [0.0, 1.0] range in and out. + */ +float srgbLuminance(Color e); + +/* + * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98. + */ +Color srgbYuvToRgb(Color e_gamma); + +/* + * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98. + */ +Color srgbRgbToYuv(Color e_gamma); + +/* + * Convert from srgb to linear, according to IEC 61966-2-1. + * + * [0.0, 1.0] range in and out. + */ +float srgbInvOetf(float e_gamma); +Color srgbInvOetf(Color e_gamma); + + +//////////////////////////////////////////////////////////////////////////////// +// Display-P3 transformations + +/* + * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1. + * + * [0.0, 1.0] range in and out. + */ +float p3Luminance(Color e); + + +//////////////////////////////////////////////////////////////////////////////// +// BT.2100 transformations - according to ITU-R BT.2100-2 + +/* + * Calculate the luminance of a linear RGB BT.2100 pixel. + * + * [0.0, 1.0] range in and out. + */ +float bt2100Luminance(Color e); + +/* + * Convert from OETF'd BT.2100 RGB to YUV. + */ +Color bt2100RgbToYuv(Color e_gamma); + +/* + * Convert from OETF'd BT.2100 YUV to RGB. + */ +Color bt2100YuvToRgb(Color e_gamma); + +/* + * Convert from scene luminance to HLG. + * + * [0.0, 1.0] range in and out. + */ +float hlgOetf(float e); +Color hlgOetf(Color e); + +/* + * Convert from HLG to scene luminance. + * + * [0.0, 1.0] range in and out. + */ +float hlgInvOetf(float e_gamma); +Color hlgInvOetf(Color e_gamma); + +/* + * Convert from scene luminance to PQ. + * + * [0.0, 1.0] range in and out. + */ +float pqOetf(float e); +Color pqOetf(Color e); + +/* + * Convert from PQ to scene luminance in nits. + * + * [0.0, 1.0] range in and out. + */ +float pqInvOetf(float e_gamma); +Color pqInvOetf(Color e_gamma); + + +//////////////////////////////////////////////////////////////////////////////// +// Color space conversions + +/* + * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1. + * + * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the + * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is + * always the inverse of the RGB gamut to XYZ matrix. + */ +Color bt709ToP3(Color e); +Color bt709ToBt2100(Color e); +Color p3ToBt709(Color e); +Color p3ToBt2100(Color e); +Color bt2100ToBt709(Color e); +Color bt2100ToP3(Color e); + +/* + * Identity conversion. + */ +inline Color identityConversion(Color e) { return e; } + +/* + * Get the conversion to apply to the HDR image for recovery map generation + */ +ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut); + + +//////////////////////////////////////////////////////////////////////////////// +// Recovery map calculations + +/* + * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR + * luminances in linear space, and the hdr ratio to encode against. + */ +uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio); + +/* + * Calculates the linear luminance in nits after applying the given recovery + * value, with the given hdr ratio, to the given sdr input in the range [0, 1]. + */ +Color applyRecovery(Color e, float recovery, float hdr_ratio); + +/* + * Helper for sampling from YUV 420 images. + */ +Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y); + +/* + * Helper for sampling from P010 images. + * + * Expect narrow-range image data for P010. + */ +Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y); + +/* + * Sample the image at the provided location, with a weighting based on nearby + * pixels and the map scale factor. + */ +Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Sample the image at the provided location, with a weighting based on nearby + * pixels and the map scale factor. + * + * Expect narrow-range image data for P010. + */ +Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Sample the recovery value for the map from a given x,y coordinate on a scale + * that is map scale factor larger than the map size. + */ +float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Convert from Color to RGBA1010102. + * + * Alpha always set to 1.0. + */ +uint32_t colorToRgba1010102(Color e_gamma); + +} // namespace android::recoverymap + +#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h new file mode 100644 index 0000000000..e35f2d72cb --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H + +#include <stdint.h> +#include <cstdio> + + +namespace android::recoverymap { + +struct jpegr_metadata; + +/* + * Parses XMP packet and fills metadata with data from XMP + * + * @param xmp_data pointer to XMP packet + * @param xmp_size size of XMP packet + * @param metadata place to store HDR metadata values + * @return true if metadata is successfully retrieved, false otherwise +*/ +bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); + +} + +#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp new file mode 100644 index 0000000000..0185e55e9e --- /dev/null +++ b/libs/jpegrecoverymap/jpegdecoder.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/jpegdecoder.h> + +#include <cutils/log.h> + +#include <errno.h> +#include <setjmp.h> +#include <string> + +using namespace std; + +namespace android::recoverymap { + +const uint32_t kExifMarker = JPEG_APP0 + 1; +const uint32_t kICCMarker = JPEG_APP0 + 2; + +struct jpegr_source_mgr : jpeg_source_mgr { + jpegr_source_mgr(const uint8_t* ptr, int len); + ~jpegr_source_mgr(); + + const uint8_t* mBufferPtr; + size_t mBufferLength; +}; + +struct jpegrerror_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +static void jpegr_init_source(j_decompress_ptr cinfo) { + jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src); + src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr); + src->bytes_in_buffer = src->mBufferLength; +} + +static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) { + ALOGE("%s : should not get here", __func__); + return FALSE; +} + +static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { + jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src); + + if (num_bytes > static_cast<long>(src->bytes_in_buffer)) { + ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer"); + } else { + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + +static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {} + +jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) : + mBufferPtr(ptr), mBufferLength(len) { + init_source = jpegr_init_source; + fill_input_buffer = jpegr_fill_input_buffer; + skip_input_data = jpegr_skip_input_data; + resync_to_restart = jpeg_resync_to_restart; + term_source = jpegr_term_source; +} + +jpegr_source_mgr::~jpegr_source_mgr() {} + +static void jpegrerror_exit(j_common_ptr cinfo) { + jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err); + longjmp(err->setjmp_buffer, 1); +} + +JpegDecoder::JpegDecoder() { +} + +JpegDecoder::~JpegDecoder() { +} + +bool JpegDecoder::decompressImage(const void* image, int length) { + if (image == nullptr || length <= 0) { + ALOGE("Image size can not be handled: %d", length); + return false; + } + + mResultBuffer.clear(); + mXMPBuffer.clear(); + if (!decode(image, length)) { + return false; + } + + return true; +} + +void* JpegDecoder::getDecompressedImagePtr() { + return mResultBuffer.data(); +} + +size_t JpegDecoder::getDecompressedImageSize() { + return mResultBuffer.size(); +} + +void* JpegDecoder::getXMPPtr() { + return mXMPBuffer.data(); +} + +size_t JpegDecoder::getXMPSize() { + return mXMPBuffer.size(); +} + + +size_t JpegDecoder::getDecompressedImageWidth() { + return mWidth; +} + +size_t JpegDecoder::getDecompressedImageHeight() { + return mHeight; +} + +bool JpegDecoder::decode(const void* image, int length) { + jpeg_decompress_struct cinfo; + jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); + jpegrerror_mgr myerr; + string nameSpace = "http://ns.adobe.com/xap/1.0/"; + + cinfo.err = jpeg_std_error(&myerr.pub); + myerr.pub.error_exit = jpegrerror_exit; + + if (setjmp(myerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + return false; + } + jpeg_create_decompress(&cinfo); + + jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); + + cinfo.src = &mgr; + jpeg_read_header(&cinfo, TRUE); + + // Save XMP Data + for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) { + if (marker->marker == kExifMarker) { + const unsigned int len = marker->data_length; + if (len > nameSpace.size() && + !strncmp(reinterpret_cast<const char*>(marker->data), + nameSpace.c_str(), nameSpace.size())) { + mXMPBuffer.resize(len+1, 0); + memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len); + break; + } + } + } + + + mWidth = cinfo.image_width; + mHeight = cinfo.image_height; + + if (cinfo.jpeg_color_space == JCS_YCbCr) { + mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0); + } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { + mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0); + } + + cinfo.raw_data_out = TRUE; + cinfo.dct_method = JDCT_IFAST; + cinfo.out_color_space = cinfo.jpeg_color_space; + + jpeg_start_decompress(&cinfo); + + if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()), + cinfo.jpeg_color_space == JCS_GRAYSCALE)) { + return false; + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return true; +} + +bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, + bool isSingleChannel) { + if (isSingleChannel) { + return decompressSingleChannel(cinfo, dest); + } + return decompressYUV(cinfo, dest); +} + +bool JpegDecoder::getCompressedImageParameters(const void* image, int length, + size_t *pWidth, size_t *pHeight, + std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) { + jpeg_decompress_struct cinfo; + jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); + jpegrerror_mgr myerr; + cinfo.err = jpeg_std_error(&myerr.pub); + myerr.pub.error_exit = jpegrerror_exit; + + if (setjmp(myerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + return false; + } + jpeg_create_decompress(&cinfo); + + jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); + jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF); + + cinfo.src = &mgr; + if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) { + jpeg_destroy_decompress(&cinfo); + return false; + } + + *pWidth = cinfo.image_width; + *pHeight = cinfo.image_height; + + //TODO: Parse iccProfile and exifData + (void)iccData; + (void)exifData; + + + jpeg_destroy_decompress(&cinfo); + return true; +} + + +bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) { + + JSAMPROW y[kCompressBatchSize]; + JSAMPROW cb[kCompressBatchSize / 2]; + JSAMPROW cr[kCompressBatchSize / 2]; + JSAMPARRAY planes[3] {y, cb, cr}; + + size_t y_plane_size = cinfo->image_width * cinfo->image_height; + size_t uv_plane_size = y_plane_size / 4; + uint8_t* y_plane = const_cast<uint8_t*>(dest); + uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size); + uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size); + std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); + memset(empty.get(), 0, cinfo->image_width); + + while (cinfo->output_scanline < cinfo->image_height) { + for (int i = 0; i < kCompressBatchSize; ++i) { + size_t scanline = cinfo->output_scanline + i; + if (scanline < cinfo->image_height) { + y[i] = y_plane + scanline * cinfo->image_width; + } else { + y[i] = empty.get(); + } + } + // cb, cr only have half scanlines + for (int i = 0; i < kCompressBatchSize / 2; ++i) { + size_t scanline = cinfo->output_scanline / 2 + i; + if (scanline < cinfo->image_height / 2) { + int offset = scanline * (cinfo->image_width / 2); + cb[i] = u_plane + offset; + cr[i] = v_plane + offset; + } else { + cb[i] = cr[i] = empty.get(); + } + } + + int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize); + if (processed != kCompressBatchSize) { + ALOGE("Number of processed lines does not equal input lines."); + return false; + } + } + return true; +} + +bool JpegDecoder::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) { + JSAMPROW y[kCompressBatchSize]; + JSAMPARRAY planes[1] {y}; + + uint8_t* y_plane = const_cast<uint8_t*>(dest); + std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); + memset(empty.get(), 0, cinfo->image_width); + + while (cinfo->output_scanline < cinfo->image_height) { + for (int i = 0; i < kCompressBatchSize; ++i) { + size_t scanline = cinfo->output_scanline + i; + if (scanline < cinfo->image_height) { + y[i] = y_plane + scanline * cinfo->image_width; + } else { + y[i] = empty.get(); + } + } + + int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize); + if (processed != kCompressBatchSize / 2) { + ALOGE("Number of processed lines does not equal input lines."); + return false; + } + } + return true; +} + +} // namespace android diff --git a/libs/jpegrecoverymap/jpegencoder.cpp b/libs/jpegrecoverymap/jpegencoder.cpp new file mode 100644 index 0000000000..1997bf9ea3 --- /dev/null +++ b/libs/jpegrecoverymap/jpegencoder.cpp @@ -0,0 +1,239 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/jpegencoder.h> + +#include <cutils/log.h> + +#include <errno.h> + +namespace android::recoverymap { + +// The destination manager that can access |mResultBuffer| in JpegEncoder. +struct destination_mgr { +public: + struct jpeg_destination_mgr mgr; + JpegEncoder* encoder; +}; + +JpegEncoder::JpegEncoder() { +} + +JpegEncoder::~JpegEncoder() { +} + +bool JpegEncoder::compressImage(const void* image, int width, int height, int quality, + const void* iccBuffer, unsigned int iccSize, + bool isSingleChannel) { + if (width % 8 != 0 || height % 2 != 0) { + ALOGE("Image size can not be handled: %dx%d", width, height); + return false; + } + + mResultBuffer.clear(); + if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) { + return false; + } + ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", + (width * height * 12) / 8, width, height, mResultBuffer.size()); + return true; +} + +void* JpegEncoder::getCompressedImagePtr() { + return mResultBuffer.data(); +} + +size_t JpegEncoder::getCompressedImageSize() { + return mResultBuffer.size(); +} + +void JpegEncoder::initDestination(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer; + buffer.resize(kBlockSize); + dest->mgr.next_output_byte = &buffer[0]; + dest->mgr.free_in_buffer = buffer.size(); +} + +boolean JpegEncoder::emptyOutputBuffer(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer; + size_t oldsize = buffer.size(); + buffer.resize(oldsize + kBlockSize); + dest->mgr.next_output_byte = &buffer[oldsize]; + dest->mgr.free_in_buffer = kBlockSize; + return true; +} + +void JpegEncoder::terminateDestination(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer; + buffer.resize(buffer.size() - dest->mgr.free_in_buffer); +} + +void JpegEncoder::outputErrorMessage(j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + ALOGE("%s\n", buffer); +} + +bool JpegEncoder::encode(const void* image, int width, int height, int jpegQuality, + const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) { + jpeg_compress_struct cinfo; + jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + // Override output_message() to print error log with ALOGE(). + cinfo.err->output_message = &outputErrorMessage; + jpeg_create_compress(&cinfo); + setJpegDestination(&cinfo); + + setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel); + jpeg_start_compress(&cinfo, TRUE); + + if (iccBuffer != nullptr && iccSize > 0) { + jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize); + } + + if (!compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel)) { + return false; + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + return true; +} + +void JpegEncoder::setJpegDestination(jpeg_compress_struct* cinfo) { + destination_mgr* dest = static_cast<struct destination_mgr *>((*cinfo->mem->alloc_small) ( + (j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(destination_mgr))); + dest->encoder = this; + dest->mgr.init_destination = &initDestination; + dest->mgr.empty_output_buffer = &emptyOutputBuffer; + dest->mgr.term_destination = &terminateDestination; + cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest); +} + +void JpegEncoder::setJpegCompressStruct(int width, int height, int quality, + jpeg_compress_struct* cinfo, bool isSingleChannel) { + cinfo->image_width = width; + cinfo->image_height = height; + if (isSingleChannel) { + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + } else { + cinfo->input_components = 3; + cinfo->in_color_space = JCS_YCbCr; + } + jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); + jpeg_set_colorspace(cinfo, isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr); + cinfo->raw_data_in = TRUE; + cinfo->dct_method = JDCT_IFAST; + + if (!isSingleChannel) { + // Configure sampling factors. The sampling factor is JPEG subsampling 420 because the + // source format is YUV420. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; + } +} + +bool JpegEncoder::compress( + jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel) { + if (isSingleChannel) { + return compressSingleChannel(cinfo, image); + } + return compressYuv(cinfo, image); +} + +bool JpegEncoder::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) { + JSAMPROW y[kCompressBatchSize]; + JSAMPROW cb[kCompressBatchSize / 2]; + JSAMPROW cr[kCompressBatchSize / 2]; + JSAMPARRAY planes[3] {y, cb, cr}; + + size_t y_plane_size = cinfo->image_width * cinfo->image_height; + size_t uv_plane_size = y_plane_size / 4; + uint8_t* y_plane = const_cast<uint8_t*>(yuv); + uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size); + uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size); + std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); + memset(empty.get(), 0, cinfo->image_width); + + while (cinfo->next_scanline < cinfo->image_height) { + for (int i = 0; i < kCompressBatchSize; ++i) { + size_t scanline = cinfo->next_scanline + i; + if (scanline < cinfo->image_height) { + y[i] = y_plane + scanline * cinfo->image_width; + } else { + y[i] = empty.get(); + } + } + // cb, cr only have half scanlines + for (int i = 0; i < kCompressBatchSize / 2; ++i) { + size_t scanline = cinfo->next_scanline / 2 + i; + if (scanline < cinfo->image_height / 2) { + int offset = scanline * (cinfo->image_width / 2); + cb[i] = u_plane + offset; + cr[i] = v_plane + offset; + } else { + cb[i] = cr[i] = empty.get(); + } + } + + int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize); + if (processed != kCompressBatchSize) { + ALOGE("Number of processed lines does not equal input lines."); + return false; + } + } + return true; +} + +bool JpegEncoder::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) { + JSAMPROW y[kCompressBatchSize]; + JSAMPARRAY planes[1] {y}; + + uint8_t* y_plane = const_cast<uint8_t*>(image); + std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); + memset(empty.get(), 0, cinfo->image_width); + + while (cinfo->next_scanline < cinfo->image_height) { + for (int i = 0; i < kCompressBatchSize; ++i) { + size_t scanline = cinfo->next_scanline + i; + if (scanline < cinfo->image_height) { + y[i] = y_plane + scanline * cinfo->image_width; + } else { + y[i] = empty.get(); + } + } + int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize); + if (processed != kCompressBatchSize / 2) { + ALOGE("Number of processed lines does not equal input lines."); + return false; + } + } + return true; +} + +} // namespace android diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp new file mode 100644 index 0000000000..c9ac92128f --- /dev/null +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -0,0 +1,750 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/recoverymap.h> +#include <jpegrecoverymap/jpegencoder.h> +#include <jpegrecoverymap/jpegdecoder.h> +#include <jpegrecoverymap/recoverymapmath.h> +#include <jpegrecoverymap/recoverymaputils.h> + +#include <image_io/jpeg/jpeg_marker.h> +#include <image_io/xml/xml_writer.h> +#include <image_io/jpeg/jpeg_info.h> +#include <image_io/jpeg/jpeg_scanner.h> +#include <image_io/jpeg/jpeg_info_builder.h> +#include <image_io/base/data_segment_data_source.h> +#include <utils/Log.h> + +#include <memory> +#include <sstream> +#include <string> +#include <cmath> + +using namespace std; +using namespace photos_editing_formats::image_io; + +namespace android::recoverymap { + +#define JPEGR_CHECK(x) \ + { \ + status_t status = (x); \ + if ((status) != NO_ERROR) { \ + return status; \ + } \ + } + +// The current JPEGR version that we encode to +static const uint32_t kJpegrVersion = 1; + +// Map is quarter res / sixteenth size +static const size_t kMapDimensionScaleFactor = 4; +// JPEG compress quality (0 ~ 100) for recovery map +static const int kMapCompressQuality = 85; + +// TODO: fill in st2086 metadata +static const st2086_metadata kSt2086Metadata = { + {0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f}, + 0, + 1.0f, +}; + +/* + * Helper function used for generating XMP metadata. + * + * @param prefix The prefix part of the name. + * @param suffix The suffix part of the name. + * @return A name of the form "prefix:suffix". + */ +string Name(const string &prefix, const string &suffix) { + std::stringstream ss; + ss << prefix << ":" << suffix; + return ss.str(); +} + +/* + * Helper function used for writing data to destination. + * + * @param destination destination of the data to be written. + * @param source source of data being written. + * @param length length of the data to be written. + * @param position cursor in desitination where the data is to be written. + * @return status of succeed or error code. + */ +status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) { + if (position + length > destination->maxLength) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + + memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length); + position += length; + return NO_ERROR; +} + +/* Encode API-0 */ +status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest, + int quality, + jr_exif_ptr /* exif */) { + if (uncompressed_p010_image == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + if (quality < 0 || quality > 100) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + + jpegr_uncompressed_struct uncompressed_yuv_420_image; + JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image)); + + jpegr_uncompressed_struct map; + JPEGR_CHECK(generateRecoveryMap( + &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JpegEncoder jpeg_encoder; + // TODO: determine ICC data based on color gamut information + if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data, + uncompressed_yuv_420_image.width, + uncompressed_yuv_420_image.height, quality, nullptr, 0)) { + return ERROR_JPEGR_ENCODE_ERROR; + } + jpegr_compressed_struct jpeg; + jpeg.data = jpeg_encoder.getCompressedImagePtr(); + jpeg.length = jpeg_encoder.getCompressedImageSize(); + + JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest)); + + return NO_ERROR; +} + +/* Encode API-1 */ +status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr uncompressed_yuv_420_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest, + int quality, + jr_exif_ptr /* exif */) { + if (uncompressed_p010_image == nullptr + || uncompressed_yuv_420_image == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + if (quality < 0 || quality > 100) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width + || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + + jpegr_uncompressed_struct map; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JpegEncoder jpeg_encoder; + // TODO: determine ICC data based on color gamut information + if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data, + uncompressed_yuv_420_image->width, + uncompressed_yuv_420_image->height, quality, nullptr, 0)) { + return ERROR_JPEGR_ENCODE_ERROR; + } + jpegr_compressed_struct jpeg; + jpeg.data = jpeg_encoder.getCompressedImagePtr(); + jpeg.length = jpeg_encoder.getCompressedImageSize(); + + JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest)); + + return NO_ERROR; +} + +/* Encode API-2 */ +status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest) { + if (uncompressed_p010_image == nullptr + || uncompressed_yuv_420_image == nullptr + || compressed_jpeg_image == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width + || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + + jpegr_uncompressed_struct map; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest)); + + return NO_ERROR; +} + +/* Encode API-3 */ +status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, + jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, + jr_compressed_ptr dest) { + if (uncompressed_p010_image == nullptr + || compressed_jpeg_image == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + jpegr_uncompressed_struct uncompressed_yuv_420_image; + uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); + uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); + uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); + uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut; + + if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width + || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + + jpegr_uncompressed_struct map; + JPEGR_CHECK(generateRecoveryMap( + &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest)); + + return NO_ERROR; +} + +status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, + jr_info_ptr jpegr_info) { + if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + jpegr_compressed_struct primary_image, recovery_map; + JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, + &primary_image, &recovery_map)); + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length, + &jpegr_info->width, &jpegr_info->height, + jpegr_info->iccData, jpegr_info->exifData)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + return NO_ERROR; +} + +/* Decode API */ +status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, + jr_uncompressed_ptr dest, + jr_exif_ptr exif, + bool request_sdr) { + if (compressed_jpegr_image == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + // TODO: fill EXIF data + (void) exif; + + jpegr_compressed_struct compressed_map; + jpegr_metadata metadata; + JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map)); + + jpegr_uncompressed_struct map; + JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map)); + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + jpegr_uncompressed_struct uncompressed_yuv_420_image; + uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); + uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); + uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); + + if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()), + jpeg_decoder.getXMPSize(), &metadata)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + if (request_sdr) { + memcpy(dest->data, uncompressed_yuv_420_image.data, + uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2); + dest->width = uncompressed_yuv_420_image.width; + dest->height = uncompressed_yuv_420_image.height; + } else { + JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest)); + } + + return NO_ERROR; +} + +status_t RecoveryMap::decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map, + jr_uncompressed_ptr dest) { + if (compressed_recovery_map == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_recovery_map->data, + compressed_recovery_map->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + dest->data = jpeg_decoder.getDecompressedImagePtr(); + dest->width = jpeg_decoder.getDecompressedImageWidth(); + dest->height = jpeg_decoder.getDecompressedImageHeight(); + + return NO_ERROR; +} + +status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, + jr_compressed_ptr dest) { + if (uncompressed_recovery_map == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + // TODO: should we have ICC data for the map? + JpegEncoder jpeg_encoder; + if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, + uncompressed_recovery_map->width, + uncompressed_recovery_map->height, + kMapCompressQuality, + nullptr, + 0, + true /* isSingleChannel */)) { + return ERROR_JPEGR_ENCODE_ERROR; + } + + if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + + memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize()); + dest->length = jpeg_encoder.getCompressedImageSize(); + dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED; + + return NO_ERROR; +} + +status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_uncompressed_ptr uncompressed_p010_image, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest) { + if (uncompressed_yuv_420_image == nullptr + || uncompressed_p010_image == nullptr + || metadata == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width + || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED + || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) { + return ERROR_JPEGR_INVALID_COLORGAMUT; + } + + size_t image_width = uncompressed_yuv_420_image->width; + size_t image_height = uncompressed_yuv_420_image->height; + size_t map_width = image_width / kMapDimensionScaleFactor; + size_t map_height = image_height / kMapDimensionScaleFactor; + + dest->width = map_width; + dest->height = map_height; + dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED; + dest->data = new uint8_t[map_width * map_height]; + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(dest->data)); + + ColorTransformFn hdrInvOetf = nullptr; + float hdr_white_nits = 0.0f; + switch (metadata->transferFunction) { + case JPEGR_TF_HLG: + hdrInvOetf = hlgInvOetf; + hdr_white_nits = kHlgMaxNits; + break; + case JPEGR_TF_PQ: + hdrInvOetf = pqInvOetf; + hdr_white_nits = kPqMaxNits; + break; + } + + ColorTransformFn hdrGamutConversionFn = getHdrConversionFn( + uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut); + + ColorCalculationFn luminanceFn = nullptr; + switch (uncompressed_yuv_420_image->colorGamut) { + case JPEGR_COLORGAMUT_BT709: + luminanceFn = srgbLuminance; + break; + case JPEGR_COLORGAMUT_P3: + luminanceFn = p3Luminance; + break; + case JPEGR_COLORGAMUT_BT2100: + luminanceFn = bt2100Luminance; + break; + case JPEGR_COLORGAMUT_UNSPECIFIED: + // Should be impossible to hit after input validation. + return ERROR_JPEGR_INVALID_COLORGAMUT; + } + + float hdr_y_nits_max = 0.0f; + double hdr_y_nits_avg = 0.0f; + for (size_t y = 0; y < image_height; ++y) { + for (size_t x = 0; x < image_width; ++x) { + Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y); + Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma); + Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma); + hdr_rgb = hdrGamutConversionFn(hdr_rgb); + float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits; + + hdr_y_nits_avg += hdr_y_nits; + if (hdr_y_nits > hdr_y_nits_max) { + hdr_y_nits_max = hdr_y_nits; + } + } + } + hdr_y_nits_avg /= image_width * image_height; + + metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits; + if (metadata->transferFunction == JPEGR_TF_PQ) { + metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg; + metadata->hdr10Metadata.maxCLL = hdr_y_nits_max; + } + + for (size_t y = 0; y < map_height; ++y) { + for (size_t x = 0; x < map_width; ++x) { + Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image, + kMapDimensionScaleFactor, x, y); + Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma); + Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma); + float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits; + + Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); + Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma); + Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma); + hdr_rgb = hdrGamutConversionFn(hdr_rgb); + float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits; + + size_t pixel_idx = x + y * map_width; + reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] = + encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor); + } + } + + map_data.release(); + return NO_ERROR; +} + +status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, + jr_uncompressed_ptr uncompressed_recovery_map, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest) { + if (uncompressed_yuv_420_image == nullptr + || uncompressed_recovery_map == nullptr + || metadata == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + size_t width = uncompressed_yuv_420_image->width; + size_t height = uncompressed_yuv_420_image->height; + + dest->width = width; + dest->height = height; + size_t pixel_count = width * height; + + ColorTransformFn hdrOetf = nullptr; + switch (metadata->transferFunction) { + case JPEGR_TF_HLG: + hdrOetf = hlgOetf; + break; + case JPEGR_TF_PQ: + hdrOetf = pqOetf; + break; + } + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); + Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr); + Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr); + + // TODO: determine map scaling factor based on actual map dims + float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y); + Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor); + + Color rgb_gamma_hdr = hdrOetf(rgb_hdr); + uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr); + + size_t pixel_idx = x + y * width; + reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102; + } + } + return NO_ERROR; +} + +status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr primary_image, + jr_compressed_ptr recovery_map) { + if (compressed_jpegr_image == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + MessageHandler msg_handler; + std::shared_ptr<DataSegment> seg = + DataSegment::Create(DataRange(0, compressed_jpegr_image->length), + static_cast<const uint8_t*>(compressed_jpegr_image->data), + DataSegment::BufferDispositionPolicy::kDontDelete); + DataSegmentDataSource data_source(seg); + JpegInfoBuilder jpeg_info_builder; + jpeg_info_builder.SetImageLimit(2); + JpegScanner jpeg_scanner(&msg_handler); + jpeg_scanner.Run(&data_source, &jpeg_info_builder); + data_source.Reset(); + + if (jpeg_scanner.HasError()) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + const auto& jpeg_info = jpeg_info_builder.GetInfo(); + const auto& image_ranges = jpeg_info.GetImageRanges(); + if (image_ranges.empty()) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (image_ranges.size() != 2) { + // Must be 2 JPEG Images + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (primary_image != nullptr) { + primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) + + image_ranges[0].GetBegin(); + primary_image->length = image_ranges[0].GetLength(); + } + + if (recovery_map != nullptr) { + recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) + + image_ranges[1].GetBegin(); + recovery_map->length = image_ranges[1].GetLength(); + } + + return NO_ERROR; +} + + +status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr dest) { + if (compressed_jpegr_image == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest); +} + +status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr compressed_recovery_map, + jr_metadata_ptr metadata, + jr_compressed_ptr dest) { + if (compressed_jpeg_image == nullptr + || compressed_recovery_map == nullptr + || metadata == nullptr + || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + const string xmp = generateXmp(compressed_recovery_map->length, *metadata); + const string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator + + // 2 bytes: APP1 sign (ff e1) + // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0", + // x bytes: length of xmp packet + + const int length = 3 + nameSpaceLength + xmp.size(); + const uint8_t lengthH = ((length >> 8) & 0xff); + const uint8_t lengthL = (length & 0xff); + + int pos = 0; + + // JPEG/R structure: + // SOI (ff d8) + // APP1 (ff e1) + // 2 bytes of length (2 + 29 + length of xmp packet) + // name space ("http://ns.adobe.com/xap/1.0/\0") + // xmp + // primary image (without the first two bytes, the SOI sign) + // secondary image (the recovery map) + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthH, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthL, 1, pos)); + JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos)); + JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos)); + JPEGR_CHECK(Write(dest, + (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos)); + JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos)); + dest->length = pos; + + return NO_ERROR; +} + +string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) { + const string kContainerPrefix = "GContainer"; + const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; + const string kItemPrefix = "Item"; + const string kRecoveryMap = "RecoveryMap"; + const string kDirectory = "Directory"; + const string kImageJpeg = "image/jpeg"; + const string kItem = "Item"; + const string kLength = "Length"; + const string kMime = "Mime"; + const string kPrimary = "Primary"; + const string kSemantic = "Semantic"; + const string kVersion = "Version"; + + const string kConDir = Name(kContainerPrefix, kDirectory); + const string kContainerItem = Name(kContainerPrefix, kItem); + const string kItemLength = Name(kItemPrefix, kLength); + const string kItemMime = Name(kItemPrefix, kMime); + const string kItemSemantic = Name(kItemPrefix, kSemantic); + + const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); + const vector<string> kLiItem({string("rdf:li"), kContainerItem}); + + std::stringstream ss; + photos_editing_formats::image_io::XmlWriter writer(ss); + writer.StartWritingElement("x:xmpmeta"); + writer.WriteXmlns("x", "adobe:ns:meta/"); + writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2"); + writer.StartWritingElement("rdf:RDF"); + writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + writer.StartWritingElement("rdf:Description"); + writer.WriteXmlns(kContainerPrefix, kContainerUri); + writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version); + writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"), + metadata.rangeScalingFactor); + // TODO: determine structure for hdr10 metadata + // TODO: write rest of metadata + writer.StartWritingElements(kConDirSeq); + size_t item_depth = writer.StartWritingElements(kLiItem); + writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); + writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); + writer.FinishWritingElementsToDepth(item_depth); + writer.StartWritingElements(kLiItem); + writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); + writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); + writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); + writer.FinishWriting(); + + return ss.str(); +} + +status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, + jr_uncompressed_ptr dest) { + if (uncompressed_p010_image == nullptr || dest == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + dest->width = uncompressed_p010_image->width; + dest->height = uncompressed_p010_image->height; + unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2); + dest->data = dest_data.get(); + + // TODO: Tone map algorighm here. + + return NO_ERROR; +} + +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp new file mode 100644 index 0000000000..e838f43b58 --- /dev/null +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -0,0 +1,437 @@ +/* + * Copyright 2022 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 <cmath> + +#include <jpegrecoverymap/recoverymapmath.h> + +namespace android::recoverymap { + +//////////////////////////////////////////////////////////////////////////////// +// sRGB transformations + +// See IEC 61966-2-1, Equation F.7. +static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f; + +float srgbLuminance(Color e) { + return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; +} + +// See ECMA TR/98, Section 7. +static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f; + +Color srgbYuvToRgb(Color e_gamma) { + return {{{ e_gamma.y + kSrgbRCr * e_gamma.v, + e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v, + e_gamma.y + kSrgbBCb * e_gamma.u }}}; +} + +// See ECMA TR/98, Section 7. +static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f; +static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f; +static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f; + +Color srgbRgbToYuv(Color e_gamma) { + return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b, + kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b, + kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}}; +} + +// See IEC 61966-2-1, Equations F.5 and F.6. +float srgbInvOetf(float e_gamma) { + if (e_gamma <= 0.04045f) { + return e_gamma / 12.92f; + } else { + return pow((e_gamma + 0.055f) / 1.055f, 2.4); + } +} + +Color srgbInvOetf(Color e_gamma) { + return {{{ srgbInvOetf(e_gamma.r), + srgbInvOetf(e_gamma.g), + srgbInvOetf(e_gamma.b) }}}; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Display-P3 transformations + +// See SMPTE EG 432-1, Table 7-2. +static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f; + +float p3Luminance(Color e) { + return kP3R * e.r + kP3G * e.g + kP3B * e.b; +} + + +//////////////////////////////////////////////////////////////////////////////// +// BT.2100 transformations - according to ITU-R BT.2100-2 + +// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF +static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f; + +float bt2100Luminance(Color e) { + return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; +} + +// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals. +static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; + +Color bt2100RgbToYuv(Color e_gamma) { + float y_gamma = bt2100Luminance(e_gamma); + return {{{ y_gamma, + (e_gamma.b - y_gamma) / kBt2100Cb, + (e_gamma.r - y_gamma) / kBt2100Cr }}}; +} + +// Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty +// straight forward; we just invert the formulas for U and V above. But deriving +// the formula for G is a bit more complicated: +// +// Start with equation for luminance: +// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B +// Solve for G: +// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B +// Substitute equations for R and B in terms YUV: +// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B +// Simplify: +// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G) +// + U * (kBt2100B * kBt2100Cb / kBt2100G) +// + V * (kBt2100R * kBt2100Cr / kBt2100G) +// +// We then get the following coeficients for calculating G from YUV: +// +// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1 +// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645 +// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713 + +static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G; +static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G; + +Color bt2100YuvToRgb(Color e_gamma) { + return {{{ e_gamma.y + kBt2100Cr * e_gamma.v, + e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v, + e_gamma.y + kBt2100Cb * e_gamma.u }}}; +} + +// See ITU-R BT.2100-2, Table 5, HLG Reference OETF. +static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073; + +float hlgOetf(float e) { + if (e <= 1.0f/12.0f) { + return sqrt(3.0f * e); + } else { + return kHlgA * log(12.0f * e - kHlgB) + kHlgC; + } +} + +Color hlgOetf(Color e) { + return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}}; +} + +// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF. +float hlgInvOetf(float e_gamma) { + if (e_gamma <= 0.5f) { + return pow(e_gamma, 2.0f) / 3.0f; + } else { + return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f; + } +} + +Color hlgInvOetf(Color e_gamma) { + return {{{ hlgInvOetf(e_gamma.r), + hlgInvOetf(e_gamma.g), + hlgInvOetf(e_gamma.b) }}}; +} + +// See ITU-R BT.2100-2, Table 4, Reference PQ OETF. +static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f; +static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f, + kPqC3 = 2392.0f / 4096.0f * 32.0f; + +float pqOetf(float e) { + if (e <= 0.0f) return 0.0f; + return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)), + kPqM2); +} + +Color pqOetf(Color e) { + return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}}; +} + +// Derived from the inverse of the Reference PQ OETF. +static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f, + kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f; + +float pqInvOetf(float e_gamma) { + // This equation blows up if e_gamma is 0.0, and checking on <= 0.0 doesn't + // always catch 0.0. So, check on 0.0001, since anything this small will + // effectively be crushed to zero anyways. + if (e_gamma <= 0.0001f) return 0.0f; + return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB) + / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)), + kPqInvE); +} + +Color pqInvOetf(Color e_gamma) { + return {{{ pqInvOetf(e_gamma.r), + pqInvOetf(e_gamma.g), + pqInvOetf(e_gamma.b) }}}; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Color conversions + +Color bt709ToP3(Color e) { + return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b, + 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b, + 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}}; +} + +Color bt709ToBt2100(Color e) { + return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b, + 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b, + 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}}; +} + +Color p3ToBt709(Color e) { + return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b, + -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b, + -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}}; +} + +Color p3ToBt2100(Color e) { + return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b, + 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b, + -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}}; +} + +Color bt2100ToBt709(Color e) { + return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b, + -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b, + -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}}; +} + +Color bt2100ToP3(Color e) { + return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b, + -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b, + 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b + }}}; +} + +// TODO: confirm we always want to convert like this before calculating +// luminance. +ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) { + switch (sdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return identityConversion; + case JPEGR_COLORGAMUT_P3: + return p3ToBt709; + case JPEGR_COLORGAMUT_BT2100: + return bt2100ToBt709; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_P3: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return bt709ToP3; + case JPEGR_COLORGAMUT_P3: + return identityConversion; + case JPEGR_COLORGAMUT_BT2100: + return bt2100ToP3; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_BT2100: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return bt709ToBt2100; + case JPEGR_COLORGAMUT_P3: + return p3ToBt2100; + case JPEGR_COLORGAMUT_BT2100: + return identityConversion; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Recovery map calculations + +uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { + float gain = 1.0f; + if (y_sdr > 0.0f) { + gain = y_hdr / y_sdr; + } + + if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio; + if (gain > hdr_ratio) gain = hdr_ratio; + + return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f); +} + +static float applyRecovery(float e, float recovery, float hdr_ratio) { + if (e <= 0.0f) return 0.0f; + return exp2(log2(e) + recovery * log2(hdr_ratio)); +} + +Color applyRecovery(Color e, float recovery, float hdr_ratio) { + return {{{ applyRecovery(e.r, recovery, hdr_ratio), + applyRecovery(e.g, recovery, hdr_ratio), + applyRecovery(e.b, recovery, hdr_ratio) }}}; +} + +Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_count = image->width * image->height; + + size_t pixel_y_idx = x + y * image->width; + size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2); + + uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y_idx]; + uint8_t u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx]; + uint8_t v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx]; + + // 128 bias for UV given we are using jpeglib; see: + // https://github.com/kornelski/libjpeg/blob/master/structure.doc + return {{{ static_cast<float>(y_uint) / 255.0f, + (static_cast<float>(u_uint) - 128.0f) / 255.0f, + (static_cast<float>(v_uint) - 128.0f) / 255.0f }}}; +} + +Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_count = image->width * image->height; + + size_t pixel_y_idx = x + y * image->width; + size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2); + + uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx] + >> 6; + uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2] + >> 6; + uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1] + >> 6; + + // Conversions include taking narrow-range into account. + return {{{ static_cast<float>(y_uint) / 940.0f, + (static_cast<float>(u_uint) - 64.0f) / 940.0f - 0.5f, + (static_cast<float>(v_uint) - 64.0f) / 940.0f - 0.5f }}}; +} + +typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t); + +static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y, + getPixelFn get_pixel_fn) { + Color e = {{{ 0.0f, 0.0f, 0.0f }}}; + for (size_t dy = 0; dy < map_scale_factor; ++dy) { + for (size_t dx = 0; dx < map_scale_factor; ++dx) { + e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy); + } + } + + return e / static_cast<float>(map_scale_factor * map_scale_factor); +} + +Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel); +} + +Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return samplePixels(image, map_scale_factor, x, y, getP010Pixel); +} + +// TODO: do we need something more clever for filtering either the map or images +// to generate the map? + +static size_t clamp(const size_t& val, const size_t& low, const size_t& high) { + return val < low ? low : (high < val ? high : val); +} + +static float mapUintToFloat(uint8_t map_uint) { + return (static_cast<float>(map_uint) - 127.5f) / 127.5f; +} + +static float pythDistance(float x_diff, float y_diff) { + return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f)); +} + +float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) { + float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor); + float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor); + + size_t x_lower = static_cast<size_t>(floor(x_map)); + size_t x_upper = x_lower + 1; + size_t y_lower = static_cast<size_t>(floor(y_map)); + size_t y_upper = y_lower + 1; + + x_lower = clamp(x_lower, 0, map->width - 1); + x_upper = clamp(x_upper, 0, map->width - 1); + y_lower = clamp(y_lower, 0, map->height - 1); + y_upper = clamp(y_upper, 0, map->height - 1); + + // Use Shepard's method for inverse distance weighting. For more information: + // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method + + float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]); + float e1_dist = pythDistance(x_map - static_cast<float>(x_lower), + y_map - static_cast<float>(y_lower)); + if (e1_dist == 0.0f) return e1; + + float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]); + float e2_dist = pythDistance(x_map - static_cast<float>(x_lower), + y_map - static_cast<float>(y_upper)); + if (e2_dist == 0.0f) return e2; + + float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]); + float e3_dist = pythDistance(x_map - static_cast<float>(x_upper), + y_map - static_cast<float>(y_lower)); + if (e3_dist == 0.0f) return e3; + + float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]); + float e4_dist = pythDistance(x_map - static_cast<float>(x_upper), + y_map - static_cast<float>(y_upper)); + if (e4_dist == 0.0f) return e2; + + float e1_weight = 1.0f / e1_dist; + float e2_weight = 1.0f / e2_dist; + float e3_weight = 1.0f / e3_dist; + float e4_weight = 1.0f / e4_dist; + float total_weight = e1_weight + e2_weight + e3_weight + e4_weight; + + return e1 * (e1_weight / total_weight) + + e2 * (e2_weight / total_weight) + + e3 * (e3_weight / total_weight) + + e4 * (e4_weight / total_weight); +} + +uint32_t colorToRgba1010102(Color e_gamma) { + return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f)) + | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10) + | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20) + | (0x3 << 30); // Set alpha to 1.0 +} + +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp new file mode 100644 index 0000000000..fe46cbad91 --- /dev/null +++ b/libs/jpegrecoverymap/recoverymaputils.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/recoverymaputils.h> +#include <jpegrecoverymap/recoverymap.h> +#include <image_io/xml/xml_reader.h> +#include <image_io/base/message_handler.h> +#include <image_io/xml/xml_element_rules.h> +#include <image_io/xml/xml_handler.h> +#include <image_io/xml/xml_rule.h> + +#include <string> +#include <sstream> + +using namespace photos_editing_formats::image_io; +using namespace std; + +namespace android::recoverymap { + + +// Extremely simple XML Handler - just searches for interesting elements +class XMPXmlHandler : public XmlHandler { +public: + + XMPXmlHandler() : XmlHandler() { + rangeScalingFactorState = NotStrarted; + } + + enum ParseState { + NotStrarted, + Started, + Done + }; + + virtual DataMatchResult StartElement(const XmlTokenContext& context) { + string val; + if (context.BuildTokenValue(&val)) { + if (!val.compare(rangeScalingFactorName)) { + rangeScalingFactorState = Started; + } else { + if (rangeScalingFactorState != Done) { + rangeScalingFactorState = NotStrarted; + } + } + } + return context.GetResult(); + } + + virtual DataMatchResult FinishElement(const XmlTokenContext& context) { + if (rangeScalingFactorState == Started) { + rangeScalingFactorState = Done; + } + return context.GetResult(); + } + + virtual DataMatchResult ElementContent(const XmlTokenContext& context) { + string val; + if (rangeScalingFactorState == Started) { + if (context.BuildTokenValue(&val)) { + rangeScalingFactorStr.assign(val); + } + } + return context.GetResult(); + } + + bool getRangeScalingFactor(float* scaling_factor) { + if (rangeScalingFactorState == Done) { + stringstream ss(rangeScalingFactorStr); + float val; + if (ss >> val) { + *scaling_factor = val; + return true; + } else { + return false; + } + } else { + return false; + } + } + + bool getTransferFunction(jpegr_transfer_function* transfer_function) { + *transfer_function = JPEGR_TF_HLG; + return true; + } + +private: + static const string rangeScalingFactorName; + string rangeScalingFactorStr; + ParseState rangeScalingFactorState; +}; + +const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; + + +bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { + string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + + if (xmp_size < nameSpace.size()+2) { + // Data too short + return false; + } + + if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) { + // Not correct namespace + return false; + } + + // Position the pointers to the start of XMP XML portion + xmp_data += nameSpace.size()+1; + xmp_size -= nameSpace.size()+1; + XMPXmlHandler handler; + + // We need to remove tail data until the closing tag. Otherwise parser will throw an error. + while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) { + xmp_size--; + } + + string str(reinterpret_cast<const char*>(xmp_data), xmp_size); + MessageHandler msg_handler; + unique_ptr<XmlRule> rule(new XmlElementRule); + XmlReader reader(&handler, &msg_handler); + reader.StartParse(std::move(rule)); + reader.Parse(str); + reader.FinishParse(); + if (reader.HasErrors()) { + // Parse error + return false; + } + + if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) { + return false; + } + + if (!handler.getTransferFunction(&metadata->transferFunction)) { + return false; + } + return true; +} + +} // namespace android::recoverymap
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp new file mode 100644 index 0000000000..b509478e73 --- /dev/null +++ b/libs/jpegrecoverymap/tests/Android.bp @@ -0,0 +1,75 @@ +// Copyright 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libjpegrecoverymap_test", + test_suites: ["device-tests"], + srcs: [ + "recoverymap_test.cpp", + "recoverymapmath_test.cpp", + ], + shared_libs: [ + "libjpeg", + "liblog", + ], + static_libs: [ + "libimage_io", + "libgmock", + "libgtest", + "libjpegdecoder", + "libjpegencoder", + "libjpegrecoverymap", + ], +} + +cc_test { + name: "libjpegencoder_test", + test_suites: ["device-tests"], + srcs: [ + "jpegencoder_test.cpp", + ], + shared_libs: [ + "libjpeg", + "liblog", + ], + static_libs: [ + "libjpegencoder", + "libgtest", + ], +} + +cc_test { + name: "libjpegdecoder_test", + test_suites: ["device-tests"], + srcs: [ + "jpegdecoder_test.cpp", + ], + shared_libs: [ + "libjpeg", + "liblog", + ], + static_libs: [ + "libjpegdecoder", + "libgtest", + ], +} diff --git a/libs/jpegrecoverymap/tests/data/jpeg_image.jpg b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg Binary files differnew file mode 100644 index 0000000000..e2857425e7 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg diff --git a/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 new file mode 100644 index 0000000000..7b2fc71bc0 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 @@ -0,0 +1,1930 @@ +ØÖÖÓÑÑÏËÈÈÈÅÅÃÃÂÅÂÁÀÁÀ½¹¶³°«¬±´³²±´¯–‘ž°¯¯±´³³´µ¹»½½½»¸¸·¹»¼½¿ÁÃÆÇÉÌÎÏÐÎÍÌËÊÊÉÊÍÌËÊÆÇÈËÊÌÌÉÈÇÆÅÄÄÅÆÇÆÆÇÇÆÅÃÄÂÃÁÁÁÁÁ¿¾¼¼¼¼½»»¸·¹¸·¶µ³²²²²²±°¯¯®®®ª©¦£¤¥¤“}vrswqnpsqqklspmjmnlknw{’™›œ›š›››™›ššœœšœžŸ¡¢¤§«¯³µ¸ºº¼¼»º¹µ²¥ž—”’”¦®´µ·¸¹º»»¼»¼¼½¾¾¾½¼º¹¸¹º»¼»½ÀÂÃÆÊËÊÊÍÍÌÌÉžºµ±©©´¶¶¶³²²³°¬©²³¯©¢¡¨±´·µµµ³›…„•¬¸º¹··³¨ ¦¯±£ÙÙ×ÕÒÑÏÎÉÇÇÈÅ¿¿ÁÄÂÀÀļ·´±¬«®±²°°§Ž›®¯¯°²²²µ¹»½¼¼¼º¸¸¹»½¾¾ÁÅÇÆÉÍÏÐÏÎÎÌËÌÌËËËËÌÊÇÇÉÊËÌÌÌÊÉÈÈÆÇÇÇÈÇÇÆÆÆÆÆÆÄÃÃÃÂÂÁ¿½¼¼½½¾»¹¹¹¹¹·¶¶µ´µ´²±²±¯¯®¨¤¢¥¤f96999:7464336753/01,..2344N“žœœššœœœœžž¡£¦«®°³µ¸»¼½½¼¼¹´®¨¡›•”–𤫳¶¶·¸º¼½½¾¼½½¾¾¾¿¿½»¹¹ººº»¼¾ÀÂÅÈËÌËÎÏÐÎÊÆÄ½¸µ±¬¨§¯·¶µ´²±²²°©®²³®§¥¥«°³¶¶µ´¯¨“‚ˆ¡³¶¸···³¨¡§°´²ÚÚØÖÓÒÐÐÍÊÇÇŽ¾½¿ÁÁÁÃÄÁ»µ²¯®ª«°°¬¥£¬“¨®¯°±°±µº½¼¼¼¼»¹¸¹º¼½¿ÁÅÈÉÌÎÐÐÎÍÍÊÊÌÍÍÌÌÌËÉÈÈÈÊËÌËËËÊÊÉÈÈÈÇÆÅÆÆÆÆÆÇÆÆÄÄÃÂÂÁ¿½¼½½½¾¼¹ºº¹¸·¶µµ´´µ´²±±¯¯¬¬§¤ªr'Rouwwvzy{zxyzyxtsvurkgce_F U¢žœ›››œœ››Ÿ ¡£¨«°±³µ¸»½¿¾¾½º·°«£›–••˜ ¨®´¶¶¹»¼½½¾¾¾¾¾¾¾¾¿¿¼»º¹¹º»¼½¿ÁÄÇÊÍÍÍÑÒÏËÉÄÁ»·´±§¨²·¶´³³³²²¯«©®²³¨¤¦°³··¶µ³®£‰–®µµ¶¸¸¸²¤ §±¶·Ù×ÙØÔÑÐÍÎËÈÅÄÃÀ¿¼º¼½ÀÁÂĺµ²¯®®©™”ž£–£ª¯°°±°²¹½½¼»¼º¹¸¹¹»¼¿ÁÄÈÊÌÎÑÑÏÍÌÊÊËÌÌÌËÌÊÉÉÈÈÊËËÊÊËÊÊÈÉÉÈÇÆÆÆÅÆÅÄÆÃÄÄÁÁÂÂÁ¿½»¼½¾½»¼»º¹·¶¶··´´µµ´²±°°®¯®«ª¥¤@: ¬ª¬©ª¨§§©ª§©¨§©¦£¦¦£•¥£¤•;:—Ÿžœ™š››œ›œœŸ£¥§«®±´·»½¾¿À¿½»¸³¬§Ÿ™•”–ž¦°³¶¹»½¾½¾½¾¾¾¿¿¿¿¿½¼»º»º»¼¼½ÁÂÇÊÊÌÏÑÑÐÏÌȾ¹´¯¯¬§«´¶¶´µ¶³²±¯ª¨³²«¨¢©¶¹ºº·¶²¬™~‹¢°±²µ·¹¶®£ ¦±··Û×Ö×ÕÒÐÌÍÊÇÇÅÁ¿¾º¸·¹¼¿Á¿¹µ²¯®®°«—‰‘œš¡©ª®²²°³º¼¼¼»»º¹¹¸¸¹¼¿ÀÄÇÈÌÎÐÑÏÎÍËÉÊËËÍÍËËÊÉÈÈËËÊÊËËÊÉÊËËÉÇÆÈÇÇÆÄÄÅÄÄÄÃÃÂÂÁ¿½½½¾¾½»º»»¹¹¸¸¹º·µ¸·¶¶³²³°°°°®ª 5M¦¡¢¡¢¤¤¦¥£££¤¤¢ ¡ £}>Ž¡ž¥I1‘Ÿœš™™›ššš›œ›œ £¦«¯²´·º»¾¿¿¿¾½»µ®©£›˜–•›£©¯³¶·¹½¾¿¿¿¿½½¾¾¿¿À¿¾½»º¼»¼¼½¿ÁÃÇÊÌÎÏÑÑÐÎÊÆÁ¼¶±«ª¨¨³µµ´´´´³²¯©§±®«¦£´¸º¹¶´°¤‰€’§«°³¶·²£ §±¸¹ÛÙ×ÓÑÓÏËÊÈÄÄÃÀÁ¿¼º¶´µ¸½ÀÃÿ¹´²°¬«£ˆˆ”›Ÿ§ª«¬°±¯°º½½½½»»º¹·¸º¼ÀÀÃÆÈËÎÐÐÐÏÍÌÊËÌÌÍÌËËÊÉÇÈÊËÊÊÉÊÊËËÉÈÈÈÉÈÇÆÇÇÆÄÃÄÃÅÄÃÂÂÀ¾¾ÀÀÀ¿½»»¼¼»ºº¹¹¹¸¸¸¸¸µµµ³³³²°¯¬5T§¡£¢¤¥¦¦¦¦¥¥¥¦¤¢¢¡ ¢T1s¥Ÿ¢F)sš›—–————˜™››ž £¦ª¯³¶¸º½¿¿¿¾¾½»¹±¦Ÿ™•–›£¨®³µ·¹»½¾À¿¾¾¾½½¾¿ÀÀ¾¾¼ºº»»½¾¿ÀÂÄÈÊÍÏÏÐÑÏÌÈÄ¿º´©§¦§°µ´´³´³³³²¯¨©±±¨¢¥°¶¸ºº·²¬ƒ‰˜¤¨¬¯²µ¶²¤¤§²»ºÚØØÕÐÏÎÌËÉÃÁÀ¿¿À¾º·²°²¶»¿Â¼¸´²®¨©ª„Œ”ž©©««®°±°¸»¼½½»»»¹¹¸»½¿ÂÄÆÆÊÏÑÑÑÐÍËÍÎÎÎÌËÊÈÊÊÇÆÉÊÌÌËËÊÊÉÈÇÈÈÇÇÇÇÈÉÈÅÄÄÄÆÄÃÄÂÁÀ¿¿¿ÀÀ¾¾½½½¼»¼»¹¸¹º¹··¶µ´´µ´²²°™2Z©¤¦¥¦¥¦¥¦¥¥¤¥¤¤¤£¢£‡HeP¡ ¡@&-Hh‰—–’”—ššœ ¤¦©°µº»½¿¿ÀÀ¿¿¾»¶®©¡œ˜•™¢ª®²¶·ºº¼¾¾¾½½½½»½¿¿À¿¿¾»¼»º¼½¿¿ÂÄÆÊËÎÏÐÒÒÎËÇ»·°«¨¦¥¦²³´´´´µ´²¦©¯¯¥ ¥·¹»»º·°§Š™¥©¬°²µµ´«¡¤©´½»ØÕÖÕÑÌÊÊÉÇÄÁ½»¾¿¿»¸³¯¯´¹½Á¿»¶²©§£…ˆŽ—¥§©«¬®®·¼¾»¹»¼»ººº»½¿ÂÄÆÇÍÑÑÓÒÏÍÌÌËÌÍÌËËËËÊÈÈÉÉËÌÍÌÊÊÉÈÈÇÆÄÅÅÆÉÉÇÅÄÄÆÅÂÂÃÃÁÀ¿¾¾ÀÀ¿¾½¾¾½»»º¹¸ºº¹¸¸·¶¶¶µµ´³±°–0b¥¦¦§¥¥¦¦¥¥¤¥¤¤£££¨_gA‘¡š8$A,&6W†—›š›ž ¤§ª®²µ¹»½¿¿ÀÀÀ¾½¼·±©£ž™—™ž¦«¯²¶¸¼½¿¿½»»¼½»»½¿¿¿¿½½»»ººº»½ÀÃÆÉÌÍÐÑÑÒÒÏËÆÀ¹³©¨¥¤¨¬°²³³´··¶±«¤¨©¢ž«¸½»¹·³® „ƒŸ¨«®¯°µ·²§Ÿ£«´»½ÐÌÎÐÒÎÊÇÆÆÅÁ¾¼¼¾¾½¹´°«¬®²·½¿½¶³®ª§¬°‰Œ” ¤¦¨ª©©ª¶¼¼º¹¹¹º»º»½¾ÁÂÅÈÊÍÐÐÒÑÎÌËËÌÌÍÍÌËËÊÈÇÆÆÈËËÊËÊÊÊÉÉÈÆÄÄÅÅÅÆÆÅÆÄÅÅÄÄÄÿ½½½¾¿ÀÁÀ¿½½½»»º¹·¹¹¹»º¹¹¸¶µ´³´³³•/l²§¦¦§§¨¨¨¨§¦¦¥¥¥¤¤—?S_8z¤”06‘}V5$.YšŸž¡¦ª¬®²µ¸¼¼¿ÁÁÂÁÀ¿½·³§¢™™Ÿ¥¬°±´¸»½ÀÁÀ¾½¾¿¿¼½¿À¿¾¾¿½º¹¹¹¹»¾ÂÄÈÌÍÑÓÓÓÓÐÍÇž·±¬¨¦¤¥ª°²²²³µ··´¯¨£§««¤Ÿ¡±¹½»·µ±ª“‰•¢ª¬¬²¶¶¯§ ¦´»¼ÂÀÃÈÇËÊÇÅÄÁÁ¿½»»¼¼¹¶²¬«¬±´º½º´°ª¦§±¨‡‹’¡£§©¨©«´»¼º»¼ºº¹º¼½¾ÁÂÆÆÊÎÑÑÑÐÎÊÊÍÏÍÌÎÍËÊÉÉÈÄÆÉËÌËËËÊÊÊÊÉÉÇÅÅÅÄÄÄÄÅÅÄÄÅÅÅÃÀ¾¼¼¼¾¿¿¾¿¿½½»¹¹¸·¸¹¹»ºº»¸·µ´³´³´”0xµª§¥¥§¨¨¨¦§§¥¤¦¦¤¥vXzvT^ ,C¢¤¡ˆ_;.2Mu•¥¥ª®²µ·»¼½ÁÂÂÃÂÀ¿»¶¯©£¡œš›£¨®²´¸»½¾ÀÀÀ¾½¾¾½¾¿ÁÂÁ¿À¿¾»º¹º»¾ÀÃÈÊÍÐÓÕÕÔÒÎÊÅÀ¹³®¨¦¥£§¬¯±±²²µ¶¶³®¨£§«¦Ÿ›¦µ»¼º·²¢†ƒœ§¯´µ´¯¤¡§µ¼½¹µ³¼ÁÄÆÈÆÃÂÀ¾¾»ºººº¶³±¬©¨©®´¹¼¶±¬¨¥§±—‰™ ¢¦©¨©«³º¼½¼»»»ºº½½¾ÀÀÄÇÉÎÑÑÐÑÎÌËÌËÌÍÍËÊÉÉÈÉÆÆÈËÌÍÍËÊËÌËËÊÉÈÉÆÅÅÃÄÅÆÅÃÄÄÄÃÁ¿¾½¿¿À¿¿¿À½½¼ºº¸¸¹ºººº»»¸¶¶´´µµº“2‚¸®«¦¤¥¦¦¦¤¤£¡£§¥¤¤qЧ¦ŒW™ƒ'LŸ›¡¡ŽjI93Cjž°µ¸»¼¾ÀÃÃÂÁÀ¿½¸³¦¡ž››ž¦«°´¶º½¾¿À¿À¿½½¾½¾ÀÂÂÁ¿¾½¼º¹»¼¿ÂÄÇÌÎÐÓÕÔÔÓÏËǼµ°ª¥¢£¤©®²³´³µ¸¸¶³®¦¢¨ª¢™š«¸¼»¸´°ª˜€…“¢ª®®¯³²µµ¬¢¡¥¬¶¼¿¶´²±·¼¿ÅÆÄÂÁ¾»½¼º¹¸µ²²¯«§¦¨¬¯·¹´®¬§£«®Š˜ ¢¤¨ªªª³º¼¿¾¼»»»»¼½À¿ÀÁÅÈÍÏÐÐÐÍÌËÌÌËËËËÉÊÊÉÈÇÇÇÉËÍÍËÊÌËÊÌËÉÉÉÉÅÅÅÅÆÆÅÄÄÂÃÁÀÀ¿¿ÀÀÀÀ¿¼¾¿¿¿½¼ºº»ºººººº¸¶¶µµ´´¼ˆ3ˆº°ª¦¦¦¥¦¤¢¡Ÿž£¤¤£ £Ÿ››ƒ$W šš› ¢žŠmR.T¶¶»½¾ÁÃÄÃÂÁÀÀ»¶±«£Ÿœ›ž¤«¯³¶¸»½¿ÁÀÁÀÀ¾½¾¿ÀÂÂÃÂÀ¾¼¼¼»¾¿ÁÅÇÊÍÐÒÓÓÔÔÐÍÈÿ¹±«§¤¢¢¤¨¬°³¶¶¹¹·¶³¬¤£ª§¡™¡²·¹¹¸´°¦†ƒ‡˜¤«®¯³´µ¸´«¢£§¸¾¿º²°¯±µº½ÂÃÃÁ¿»»»ºº¹¸³±¯¬§¥¤¥§¯³¶³¯«¦¨´ª‡”¢¥¦©«©ª±·»¾¾¾¼ºº¼»¾¿ÁÃÃÆÈÍÐÏÐÎÌËËÌÌËËÊÊÉÊÉÉÈÇÆÇÊÊÌÌÊÈÊÉÊËÌÊËÉÈÇÇÇÅÄÅÅÄÅÄÄÂÀÀ¿ÀÀÀÀÀ¿½½¿¿¾¿½º»»º¹¹¹¸¸¸··¸¶´²¼5Œ¹°®«ª©§¥£¦x𣢤£¤£¡žœœž€ ^£œœ››i‡¬¥X/˜º¹¼¿ÁÁÂÄÃÂÁÁ¾¹³®¨¢œ›¤ª¯²µ·º¼¾¿ÁÀÁÀÀ¼»¼¾¾ÀÁÀ¿¿½½½¼¼¾ÁÄÆÊËÎÐÒÔÔÔÑÎÊÆÂ¾·¯©£¢¤¥¦ª®°´·¸¸¹¸µ°«¥£¨¦œ™©´·¹¹·³ªš€‡œ¨¬°´´¶·²¨ ¤©¯ºÀÀÁ´¯°°±µ»¾¿ÀÀ½»¹¸¹»º·¶³°«¨¤ ž ¤«²·µ¨¦¬¸™Œž¢¥©«ª¨®¸»¼¾¾¼¹»»º»½ÀÃÅÇÊÍÎÍÎÏÍÊÉËÍÊÌÌÉÉÊÊÊÇÇÇÈÊËËËÊÈÉÉËËÌÍËÊÉÉÇÅÄÃÅÄÃÄÄÄ¿¾¿¿À¾¾¿¿¿¾¾¾¾½»¹ºº¸¹¸¸¹¸¶¶¶¶¶µ±¹s2’·°¯¬«©¦¤¦—G#L—¥£¤¤¤¡Ÿžœt g¤›œœŸi1t¯¨³o0”¼º½¿ÀÁÁÁÁÁÀ¿º´°ª¦ œ¢©®±´·¹»¿¿ÀÁÀÁÁ¿»º»¼¼½¾¿¾½½¾¾¼»½ÁÄÆÊÍÐÓÓÕÖÒÎËÇþº²ª¥¢£¦¦¨¬°²¶¸¹¹¹¶´®ª¢ ¤ —°µ¹¹·¶°§Ž‡”¢«°³¶·¶°¤Ÿ¤¨°»ÁÁɺ±°±°²µº¼¾¿¿¼¸··¹¸¸¶µ²¯©¥žšš¡§±¸µ¬¨§¯°‘›¢¥§ª«¦¬¸¼¿¿¾½»»¹º»¼¾ÁÄÇÊÌÍÍÏÏËÉÈÈÊËËËÊÉËÊÉÈÆÈÉÉÊÌËÉÉÊÉÊËËËÊÊÊÊÈÆÆÄÄÄÄÄľ¾À¿¿¿¿ÀÀ¿¾¼¾¾¼ººº¹¹¹¶¶¹¹·µµ¶µ³±¸j.—´°¯®«¨¥¥›B!)#JŸ¤¥¥£ žŸž n"r¤£zOXw±¬²Z;¬½»½¿ÀÂÁÁÂÁ¿»·²¬§¢Ÿœž¤¬¯³··º½ÀÀÀÀÁÁÁ¼»¼¼»½½½½½½½½½¼¼¼ÀÄÇÊÍÑÔÔÕÓÏÊÉÄÀ¼µ®¥¡¡£¦§©«¯²µ¹º»¸¶³¬¥ Ÿ œ™¨¶¹¹¶´±¬‚‚Œ™¥©¬®²¶·¹´£ £«´¾À¿Ïµ²±°±´·¹º¾¿½¹¸·µ´¶¶µ³²®¨¡™—˜š£¯¹¹¯¬¶¢•££¦ªª¨¬¸¼½¾½¼½º¹»¼½¾ÁÄÇËÌÎÐÑÏÌÉÊÉÊËÊËËËÉÈÉÈÇÈÉÊËÍÌÉÊÊÉÊÉÉÉÈÈÉÉÊÉÇÅÄÃÄÄÃÁ¿¾¾¾¿ÀÁÀ¿À¿¿¾¾¼»»»ºº¹¹·¸»»·¶¶µ³²±¶b/𴝱°°©§¥M'()%dª¤¤£ ¡¡Ÿ¢j"}¥ž¤‹EŒlwµ²¥?`½½¿ÀÂÃÂÁÀ¿¼¸´©¦ žž¢¨¬²µ¸¹»¾ÁÂÁÀÀÀ¾»»½¾¼½½½½¼¼¼»»º½½ÀÃÈÌÎÒÓÔÓÐÍÈÅÿ¹±¬¥¡ £¥¦©¬°´¶¹º¹¹¶´¯¨Ÿœœ˜°¹»¹·´¯¥Œ‚ˆ‘ž¤©¬¯´¹¸¸´©¢Ÿ¤®·¾Á¾ÏȺµ´²´µµµ¸¹»»º¸¶µ´³³µ²³°«¦ ™–““›¡¨µ»·³¶¶› ¢¥¨©¦ªµº¼¾½»»º¹º½¿ÂÃÅÉÍÎÎÐÐÎÌÊÊÊÉÉËÌÍÍËÉÊÈÈÈÇÉËÌËËËËÌËÊÉÈÈÉÊÈÈÉÇÆÃÁÁÂÁÁ¾½¾¾¿ÀÁÁÁÁÀÀ¾½¼»¹¸¸¹¹¹º»ºº·µ¶µ³³³·^4³®°°°¯ª¬u%&&'&/•¥£¤££ Ÿ¥e'…¥¦•IEzXy´·|5•ļ¿ÀÂÃÃÃÂÀ¾º´®©¤¡ŸŸ¢§¬²¶¸»¼½ÀÃÄÂÀÀ¿½º»½¾¾¿¾½½¼¼¼¼»»½¿¿ÃÊÎÐÓÓÒÐÍÈÅÃÀº³®¨¥¡¡£¤¥©¬±¸º»»¹¹¶³¥›š™£³º»¹·³«œ‚‚‹–¢¦¨±¶·¹¸±¤¢Ÿ¥°¹¿Á¿ÐÍÀ·¶¶¶´µ¶¶·¸¹··µ´µ³²²³´³°«¥Ž’–›¥³½¿¾¹¦› £¤§§©´¹»¼¾¼»¼¼»¼¿ÃÄÅÊÍÎÏÐÐÏÍËËÊÉÉËÍÎÍËÊÊÈÈÈÈÇÈÉÊÌÌËÌËËËÊÈÉÉÈÇÇÇÅÄÂÂÁÁÀ¾¾¾ÀÁÀÀ¿¿ÀÀÁÀÀ¿¼¹¹ººººº»º¸¶´¶µ´´´¸[9¡±®¯¯°®«¥B%)'&'++y¬¤¥¤£¢Ÿ¥^-§¦dm…g@}¸µPJ¹ÁÀÁÂÃÃÃÄÂÁ½µ°ª¦¢¡Ÿ¡¥ª®´·º¼¾ÀÃÄÄÃÁÁ¿¼»¼¾¿¿ÀÀ¾½½¾½½½»»½ÁÅËÏÑÔÔÑÏÊÆÃ¿»´°«¥¢¡¢¤§§¨«²·ºººº¸µ²«£›™—™©¶¹º¸³¬¦“…™¤§ª¯³¶¸º·¯¦¡¢©³º¿ÀÀÑÏĸ··¸·¶µ´´¶¶µ³³³²³³²²²´³¯¦¢”‰‰Š‘•ž½Æ¿«˜ž¡£¥¤§²·º»½¾¼½½¼½¿ÂÅÇÉÌÍÏÐÑÎËËÊÉÊÊËÌÌÊÊÊÊÊËÊÊÉÈÉËÍÍËËÌËÌËÉÈÉÊÈÈÇÅÄÂÂÁÁÁÀ¿¿ÂÃÂÂÁÁÁ¿ÁÁÁ¿»¹º¼»»º¹¹·µ´¶·¶´´´¶X>£¯®®®¯¯Ÿ9,+)))0.v¬¦§§¥£¢§Y2–¨¢—®±µq‚½Ÿ8}ÇÂÁÂÃÄÄÄÃÁ½¸±©¦¢ Ÿ¥©¬°³·¹»¿ÃÃÅÃÂÀ¾½»»½¾¿ÁÀ¿¾¼¾¿¿½½¼¾¿ÄÉÍÏÒÓÒÐÎÈÄÁ¼¶±¬§£ ¡¤¦§§©ª²·¸º¼¹·´¯ª¢™–“ž±ºº¹µ±«œ„‚‡’ž¦§¬²¶¶¸¸µ¬¤œ£µ¼ÀÀ¾ÒÒʺ¹¸»¹·´³³³²²¯¯±³³´³±°±³²¬§ –Іˆ‰“•Ÿ´«—™ ££¢¥¯¶¹»½½½½¾¿ÀÀÁÅÈÉÊÌÎÎÏÌËÊËÊËËËÊÊÈÉÈÉÊËÌÊÈÇÉËÍÎÍËÌËËÊÉÇÇÉÉÈÇÅÅÅÃÁÁÂÀ¾¿ÂÄÃÃÂÁÀÁÁÁÁ¼»ºº»½»¹¸¸µµ···¶´µ¶µQB©¯«¬¬ªZ04*+57F—§¦§¦¥¥¤§T6ª®³”›½lA®ÃÀÀÁÂÃÃÂÁ¾¹´¨¤¢ Ÿ¢¦«¯²´¶¹¼¿ÂÄÄÃÀ¿¼¼»¼½½¾¾½½¼»¼½¿½¼½ÀÄÇÌÏÑÑÐÏÎËÈþ¹³®©¥¡ ¢¤¦¦§ª³·º»º¸¶²®¨¡™“•§¶»»¹³¨’€†‹š¢§«°µ··¹¸²§š¢°¹¾¿À½ÏÒξ¸º»º¶µ´³²³±¬®±±³²°¯®®±¯¬©Ÿ‘†„‡ŠŒ•›—–ž££¢£¶º»¾¼¼¼¼¿ÀÀÂÄÈÊËËÍÎÎÌÉÇÉÊÉÊÉÊÊÉÉÊÊÉÊÌÌÈÈÉÊÌËÍÍËÊËÊÈÈÈÇÉÈÆÅÅÄÂÂÁÂÁ¿ÀÀÃÃÃÂÁÁÀ¿¾¿¿¿¾½½½¼ºº¹··¸¹¹¸¶¶µ²LI®®¬¬ª®œme=1bm¥£¦©¨¦¤¤¦O<¤¯¯¯±²³·ªAjÆ¿ÀÀÁÂÂÂÁ¿¼·±«§¢¡ŸŸ¡§°³¶¸º½ÀÁÂÂÀ½½»ºº¼¾¾½»º¹ºº»»¼½½¾ÂÅÉÌÏÑÒÏÍÍÊǽ¶¯ª¦¢ ¡¢¤¨¨¨«°µ¹º»º¸µ±¬¦ ˜“¯¸¸¸µ±©Ÿ…‚Š–Ÿ¦©±¶··¸µ©Ÿ˜ž¦°·¾Á¾ÏÐÏź¼»ºº·¶³²²°«©ª®®¯®«¬®°®¬§‹‚ƒ…„‡”–‘œž¡ ¡¨³¸»¼»¼½½¿¿¿ÁÅÇËÍÎÎÍÍÊÊÈÉÉÊËÊÉÉÊËÊÊÊÊÌÌÊÉÉÊËËÌÍÌÊÊÊÉÈÈÈÉÈÆÃÂÁÁÁÁÁÀÀÁÀÂÁÁÁÀÁÁ¿¾¿ÀÁ¿½½½½»»»º¹·¹¹¹·µ²¯FO°®®°¯¯¬°³¤H7–£ ¥¥§§¥£¦ªLB«±±°±²³µ½‡:›Ä¾ÂÃÂÃÄþºµ°ª¦¢¡ ¢¦«°³¶·º¼¿ÀÀÁ¿½¼¼¹·¹»»º»¸·¶¸¹º»»¼½¿ÄÇÊÌÎÐÑÐÍËÈÄÀ¸±¨£ŸŸ¡¢¥¨§¨¬´¸ºº¼»¸µ±¬¦ž””¦µ¹·µ²¬¤“‚…–¡¨¬¯µ···µ®¤œš ¨®¹¾Â¼ÎÒÐȼ¼»ºº¹·¶µ³®©¤¢¤©«««ªªª¯¯ª£—‡€‚ƒƒ‡Œ‘Œ”›œž¨²·»½¼»»½¿¿ÀÂÅÇËÎÐÐÏÍËËËËÊÊÊÉÉÉÉËÊÉÈÊÍÍËÉÊÊÉÊÌÍÌÌÉÉÉÈÈÇÈÉÅÃÂÁÁÁÁÀ¿¿¿À¿¿¿¿ÀÁÁÀ¿¿¿¾½¼½½½¼¼¼»¼·µµ´³²±§;Q²¬®¯¬¬¬®ˆ>8}§ £¤¦¨¥¥§ª®IC¯²²±²´¶¸¸XV½À¿ÂÂÃÃÅÂÀ¼µ°¨¥¢¢¡¤ª±´·¹¼¾¿¿ÀÀ¿¾¾»ººº»ºº¹¸¹··¹ºº¹»»¾ÂÆÊÍÏÑÐÐÍÉÄÁ»µ±«§¡žž ¡£¦§¨¬³¶¹¹»¹¸¶²«¤ž–¬¶¸¶³°¨ž‡…†š ¦¬´¹¹·¶³¬ ššŸ©±º¿À¿¼ÏÏÏÊ¿»¼»¹¸¹·µ²© Ÿ £§©¨¨¨«¬®°±¬§ ’‚}|€„‰ˆ™™™›§²·»½½½¼½¿ÀÂÃÄÇÈÊÍÑÏÌÌÌÌÌÍÍÌÊÉÉÊÉÉÉÆÈÊËËÈÊÊÊËËÍÌÌËÊÉÈÈÈÉÈÆÄÃÂÂÁÂÁÀ¿¿¾½¾¾¿¿ÁÁÀ¿¿¿¾½½½¿¿¿À¾½¼¸·¶´²±±¥7W¶®¯°±¯®®¯—|z¦¢£¥¦§¥¨«®¯GJ³²³³µ¶¶¼›;†ÈÀÂÃÅÄÃÿ»²¬¨¥¤£¢¤©¬¯´·¹º½¾¾¿À¿¾¿¿¼¼»¼¼¼º¹¸¹¹¹º»¼ºº½¿ÃÇÊÍÐÑÑÎËÅÁ½µ°¨¥ Ÿ ¢¢¢¤¦©®´¶¹ººº¸µ°©£›¡´¸¹µ±¥‘„Д𠦱·¸¹¸¶±¦ž››¡«³¼¿¿¾¼ÎÍÍ˺¼¼¼¸¶µµ²¬¨¢ Ÿ ¡¡ ¤¨«®°®¬© {|~€†Œˆ‰–™–—¢°¶¹¼¼¼¼¼½¿ÁÃÄÆÈÊËÍÏÍËÌÌÌÍËËËÈÈÈÈÇÈÇÉÊÉÉÉÊÉÉÊÌÍËÊËÊÈÉÊÊÊÉÈÄÄÃÃÃÃÂÀÀÀÁÀÁÁ¿¾ÀÀÁÀ¾ÀÁÀ¿¿¾¾¿À¾¾¼¹¹¸µ´´µ¤5c¹®°±²¯®°°°²µ²ª¥¥¦§©¨§©¬±°EP¶²²³¶¶·¿oE³ÅÂÃÄÆÆÅľ·¯ª¥£¡£¥¦«°²µ·¹¼¾¿ÀÁ¿¾¿¾¾»»»»»»¹¸¹º¹º½¾½¾ÀÂÅÇÈËÎÑÒÏÌÉÄ¿º±¬©¦¤ ŸŸ¡¢¢£¤©¯³·¸¸¸·µ³§¡™˜«´¶´²®¦œƒŒ—¤«·¸¹·¸´ª¢™šž¥®¹¾¿¾¾¹ÑÐÒ̹¼¼¼¹·µ´°«©¥¡¡—•——›ž£¦ª®°±¬¢‹~zz|}€ˆ‡…“”““Ÿ®µ¹º»½½¼»¼¿ÁÂÅÈÊÌÍÍÌËÌÌÌÌËÌËÈÇÈÇÆÈÇÉÊÊÊÊÉÈÉÈÉÊÊÉÉÉÉÉÉÊÊÉÈÇÄÄÄÄÃÂÀ¿ÀÁÂÀ¿¿¾ÀÀ¿¾¿¿ÀÀÁ¿½¾¿¾½¾»¹¹¸¶´µ¶ 6p¼ªª³²¯¬°°°²¯©§¨©©©¨¦§«²¯AWº³²´µµº²DlÇÁÂÃÅÇÆÅÃÀ»´¯ª¦¥¤¤§©±´¶¸¹¼¾¿ÀÀ¿¾¿¾»¸¹º»»ººº»¼¼¼¿¿¿ÂÆÇÈÉÉÌÐÔÒÏÌÇÁ»µ°«§£¡Ÿž ¢¢¦«°³µ·¸¸µ´²§ šž®²²±±©¢{‚„Œ”¦²·¸¸¸¶±§žšœŸ¨µ½¿¿½»¸×ÔÓÏȼ¼»··¶´²°¬©¥¢¡œ”Ž”™œ¡£¦©¯°¯® ‹yyy{|~„’ž¬±µ¹º½½¼¼¾¿ÀÂÅÈÊÏÏÎÍËËËËÌÌÍËÉÈÇÆÆÆÈÉÉÉÉÉÉÊÉÈÉËÌËÊÉÉÉÉÉÊÉÇÇÆÇÆÅÅÃÁÀ¿ÁÀÀ¿¾¿ÁÁÀÀÀÀÀ¿À¿¿ÀÀ¿¿¿º¹¸¸·¶¶¹š2yÁ™m¹³²}£±¯°°°®«¬«¬ª©¨§ª¯³<^¸²²µ¶¶Â8¢ÈÄÄÆÇÇÅÃÁ½¶°¬©§§§§«¯²¶¹ºº¼¿ÀÀÀ¾¾½¾¼»¹º»»»»¼¼½¾¾¿ÂÁÂÅÇÈÊÌÍÐÑÒÐËǽ·±®«¨¤¡ŸŸ Ÿ ¢¤§¬°³´··¶´²°¬§¡œ¥®°°°¬¥™…€ƒ…Ž˜¢®¶¹¸¸ºµ¬¤œœ¡ª¸¿¿À¿»¶ÚÖÓÐʽº¼ºµ´²°®©¦£¢œ•Љ‹‹“•–ž £§©¬ª§œ†{yz{z~|‡Žª¯´¹¹¼¼½¾¾ÀÁÁÅÊÍÐÒÑÎÌËËÊÊÊÉÉÇÇÆÅÆÇÇÉÈÉÉÈÉÊÉÉÊËÌËÊÉÉÈÈÉÉÈÇÆÆÆÆÆÅÂÁÁÀÀ¿¿À¿ÀÁÀÀÀ¿ÁÀÀÀ¿¿¿ÀÂÁ¿»º¹º¹¸¸¼”.|Á²Ro‚wc²±±²²°®¯¯®®«¬©§®³¶ª;c¶±³·¹½Á]ZÈÅÆÇÈÇÆÂÀ¾º³©©¨§§«®²¶·¼»¼¿ÁÀÀ¿¾¾½¼»»»»¼¼¼¼¼½¾½¾ÂÄÃÃÅÇÉËÌÎÎÏÏÌÇÁ½·±®«©§¢ž ¥¥¨±´´´¶µ³²¯«¨¡¦®®¬¦¢€„†Œ•Ÿ©²¸¹¹¹·¯¥Ÿœ›œ¤®¹¾ÀÀÀ»´ÜÙÓÎÈÀ¹º¹·´±¯«©¦¤¤ ˜‹ˆ‡ŠŽ“——›Ÿ¢¡¤¢•}yyyxywŠŒŽ™©¯´·¸¹º½¿ÀÂÂÂÆÉÍÐÑÑÏÎÌËÌÊÉÉÈÇÆÆÇÇÈÇÉÈÉÉÊÊÊÉÉÊÌÌËÊÊÊÉÉÉÉÈÇÅÅÅÆÆÅÂÁÂÂÁÁÁÁÂÂÂÁÀÀÂÂÁ¿¿¿ÀÁÁÁÁ¿»»»»ºº¸½“.ƒ¿¼iPsBй²²³²±°±±¯««©«±³µ¦7f¸²¶¹¼Ä¨A“ÌÄÆÆÆÅÄÁ¾º¶°©§§¨©ª¯±´µ·»»¼¾¾¿Á¿¾¾¼»ºº¹»¼¼¼»º»¼½¾ÁÃÃÄÆÇÈÊÊÌÍÎÎËÆÀ»µ°¬©§£ œ›œž¢¥¨¬¯²³³³´³³±«¦¡Ÿ§«¬©£™…€ƒŠ‘›¦¯µ¹¹¸·°¨¡œš›ž§³»ÀÀ¿¾»´àÝÖÍÆÀ¹¶µµ³¯«ª§¥¤¥¢˜††…ˆ‰ˆŽ’˜—Œ|yyxvsz†ˆ–§®±µ¶¸¼½¾ÁÃÄÅÇÊÏÒÐÏÏÎÍÎÎÌËÌÌËÊÇÇÉÉÈÈÇÇÉÉÉÉÈÇÉÊËÊÊÊÉÉÉÉÉÈÅÅÆÅÄÄÄÁÁÁÁÁÁÁÂÂÃÃÂÂÃÃÃÂÁ¿¿¿À¿¿½¼»¼¼¼»»¸¾Ž/ˆ»¸dž_ª³³²±¯°±²³²¯««®²³µ¡2h¸²·½¾ÄpRÀÈÅÃÅÆÃÁ¾¾º³®©¦§¨ª±²µ¶·º½¾¾½¼½¼¼¼ºº¹¹¹º»º¹º»º½¿ÀÂÃÄÅÆÉÊÊËËÍÍÊÇý·±¯¬¨¥¢ž›››œŸ£¦ª®°±±²²²±²±®«¦Ÿ §©ª«¥Ÿ‚…Œ”¡ª²·¹¸¸³¬§Ÿœ›œ¡ª·½À¿½¼¹±ÚÙÔËĽ¸¶¶´²°®¬«¦¢ ¤¤ 𓉄ƒ…ˆˆ‡‡‡‚€~‡‹Ž„{yxrlxƒ„Š”¦¬°´º¼¼¾ÀÂÃÆÉÌÏÒÒÐÏÏÏÎÎÍÍÍÌÌÊÈÇÆÇÈÈÇÆÇÉÉÈÇÇÈÈÈÉÊÊÉÈÉÈÉÈÇÆÆÆÄÄÃÀÀÁÁÁÁÁÂÂÃÄÃÄÄÅÄÄÂÀ¿¿ÀÀ¼»»»»»»¹¹·»Š.ޏµªQa~·±²²°®¯°²³³²°°¯²´´±˜0l¸´º¾Â°EˆÍÄÅÃÅÆÃÀ¾»·²ª©©ª¬¯²µ·¹»¼½½¾¾¼»»º»»º»¹º¼¼½»»½¼¾ÁÃÄÅÆÇÈÊËÌËÌÌÊÇÿ¸³©§¦¤ šš›œ¡¥¦ª®¯°²³²±¯°¯©§Ÿ §¨¨¨¡˜…ƒ‰’œ¤®¶¸·¸¶°«¥ž››ž¦°¸¾ÁÁ½¼·¯ÉÉž»¸¶·´±¯ª¦¡¡¢¢¡œ–Œƒ€ƒ…††„€{yxwx~€|vvtlv„‚†”¤««°µº¼½¾¿ÀÂÆËÏÐÒÓÑÏÎÏÎÎÎÍÌÊÉÉÈÈÆÆÇÇÇÈÉÉÉÈÇÈÉÊÉÉÊÈÈÈÇÈÇÆÆÆÆÆÄÅÁÀÀÁÁÁÁÁÃÃÃÃÃÃÅÄÅÄÁ¿¿½¾¾»»»»»»º¹º·¼ˆ-¹¯µ_@¨µ³²²°¯±±²³³³³´µµµ²²+r¸¸¾¾ÇzL¸ÅÃÄÅÅÄÿ¼¸µ²®ª«¬¬¯²µ¹º¼½½¿¿¿¿¿¾¾¼º»½½¼¾¿¿¿½½½¾¿ÂÄÆÇÈÉÊËÌËËËËÉÅÁ¼µ¯©¨¤¢Ÿ›˜™ž¡£¥¨¬¯±±±²°¯«©¤ž ¤§§¤Œ‡Œ˜¡«µ¸···´°© š™› ©²º¾¿¿¿¼´ª¼»¹¹¹¼»µµ²°®¬¬ª¦¢¢ žš•†€€ƒ…ƒ€{wtstwvwvtrojq‚ƒ‚¡ªª®´·»¾ÀÀ¿ÂÅÊÎÐÑÑÎÏÎÍÍÍÌÌÌËÉÈÇÆÇÅÆÈÈÉËÊÊÉÇÈÊËÌÊËËÊÉÇÆÅÅÅÅÅÅÅÄÁ¿¾ÀÁÁÁÂÂÁÁÁÁÂÀÁÂÁ¿¾¾½¼½½¼»¹¹»º¹¸¶¹‚-髆t³®®°¯¯°¯±²³³²´¶´³²´Š,~À»¿ÀµItÇÁÅÆÇÆÃÀ¼º¹´°¬ªª«±´¸¹¼¿¿¿ÁÂÁÀ¿ÁÀ¿½½¿¾¿ÁÀÀ¿¾¾¾ÀÂÅÅÇÉÊËËÌÌËÉÈÇÆÄ¾¹±«©¥¢Ÿœš˜šŸ¡¤¦¦©¬®®¯±±°°®¬¬«£ž¢¦¤ –†€ƒ‰“œ¦°µ¸µ·¶³ª¢œšš¥·¼¿¿¿½º±¥³±³³µ·º·°°¯««©¦¡ Ÿœš˜—”Œ€‚}ytrrrrqoqpmifm‚‚‹Ÿ¬©«±·¼¾¿ÀÁÄÅÈËÎÐÏÏÎÌËÌËÊÊËÉÆÅÆÇÆÆÇÈÈÈÊÊÉÈÈÈÉÊËÌËËÊÊÈÆÇÇÆÅÅÅÇÅÂÀ½¾ÁÂÁÂÂÁÀÂÁÁÀÁÁ¿¾¼½½¼½½»º¹¹»¹¸¶³´’.W§«©¬±°®°°®¯±²³´²±±±³²²¼‚2–Á»½Å–:£ÈÅÆÅÅÅþ»¹¸´°ª©ª±µ¹¼¾ÁÁÂÃÂÃÄÄÄÄÃÃÃÁÀÁÂÁÀ¾¿ÀÁÄÅÇÆÇÈÊÌËËËÊÇÆÄ¿¹²¬¨§¤¡š˜™›œŸ¢£§ª®®®¯²²°°®««§£››Ÿ¢¡™‚‡™¡¬´¶¶¸¶³¤žœ›ž¨²¹½ÀÀ¿¼·®¤®®¯°²µ¸·±«ª©©¥Ÿœžœ˜““„}€}vtrppnkkmkihch{€ƒŠ›«««¯´¹¼¿ÀÃÅÇÊËÍÏÐÏÌÍÌÊÉÉÈÉÈÅÅÅÄÅÇÈÈÇÈÊËÉÇÇÈÉÉÊËÌËÊÉÇÉÈÈÈÇÅÆÆÅÂÁ¿¿ÁÂÂÁÁÁÁÁÂÃÁÁ¾½½¼½½¾¼»ºº¸º»¹¸¶´²¯T'BWcltz‡”› ¥§©©®¯°¯¬¯®® RI³À½½ÆmLÀÅÇÆÅÄÿ¼º·µ²®«ª¯²¶»¾ÁÄÄÅÇÅÅÇÇÆÇÇÆÅÃÁÂÅÃÁÁÂÂÄÅÅÆÆÈÇÉÊËÊÉÈÅÅÁ¾º´®¨¥¦¢Ÿš™˜šœ £¤¨¬®¯°¯®°±°°®¬©§¢™œ ¡›ƒ…‡’¦²·¶·¶³®¦¡ž››¢¬´»¾ÀÀ¾º´©¢®®®®¯²µ¸´«ªª¨¥ ™š››˜“‡€}~~|zxuromkjihggeaev~‚Š›¥§ª±µº¿ÀÃÅÉÌÎÐÑÑÎÌÎÍËÊÉÉÈÇÅÄÄÄÄÆÇÉÈÆÉÈÆÆÇÈËËËÊËÌËÊÈÊÉÉÉÈÇÇÇÇÃÂÁÁÂÂÂÂÁÀÀÂÂÂÀ¿¼½½½¾¿¿¾ººº·ººº»¹µ²µ¦eD;7630*0348;<?DEKPROLQSSDE‘ÃÀ¿ÁµIwËÅÆÅÅÄÃÁ¾»¹¶´±¬¬¯³¶º¿ÂÅÆÇÇÈÇÇÆÆÆÇÈÇÅÄÃÂÄÃÁÂÅÅÆÇÈÇÇÈÇÉÉÊÈÇÄÂÀ¾¹´¯¬§£¡Ÿ›š™™››ž¢¤¦«®®®¯°¯®®©¥ ™›–Š~ƒ…Œ—¢®³¶¶¶µ°¦¢ ›šŸ©²·¾À¾¾»¶°§¡¯¯®®®°±¶¶©¨§¥¢žš˜˜–•––‘Œˆ€|}}y{ywspmjiigefb\`r{€ˆ˜¤§§«¯²¸½ÁÄÆÈÌÎÏÑÑÏÍÍÎÎÌÊÈÈÄÃÄÃÃÄÆÇÈÇÅÈÈÇÇÇÈËËÊÊÌÌËËÊÊÉÉÇÇÇÉÈÉÆÄÂÅÄÃÃÃÃÁÁÂÁÀ¾½»¼¼¼»¼½½ºº¹¸º¹º»¹·µ²³±¤™’Ž‚`=243.6IOPOSWY]^ehy¦ÆÁ¿Ç@¥ÉÃÅÄÄÄÿ¼¹¶³±¯¬¯³µ»ÀÄÆÉÉÈÉÉÈÇÇÆÆÈÈÇÅÄÄÃÃÃÂÃÆÇÈÉÉÉÉÈÈÉÊÊÈÇÁ¿½ºµ±ª§¦¤ œšš››››ž¢¤§¬®¬«¬¯°°¯¬«©¥ž˜š›˜“ƒ~†œ©±µµ¶·²¨¢Ÿž›š£¶¼¿À¾½¹³¬¨£µ±¯¯®®¯²·²¨¥£¡Ÿš˜——“‘““Žˆ~|{{zwsomjihggcZV\nw‡•£§¨©¬¯µ»ÀÅÇÌÎÏÐÐÎÍÍÌÌÍÊÉÆÄÂÂÂÀÂÃÄÆÆÆÇÈÈÇÆÇÉÊÊÊÊÊËÉËËËÊÊÉÉÈÉÈÇÆÄÅÆÅÅÅÅÅÃÂÁ¿¾¼¼»»¼½¾¾¾½»ºººº¹ºº¹¸¶¶´´µ·µ·º»´šuT?2>X‚Ÿ®¶¸··¸º¾ÃÇÂÂÃÂÅdZÇÇÆÆÅÿ¼º¸³¯¯¬ª«°´¸ÀÆÈÉÊÊÊÊÊÉÈÇÉÉÊÊÈÆÅÅÅÅÄÄÅÇÇÇÉÉÊÊÊÊÊÉÇÅÃÀ¾º·³¯«¦¤£¡œšššž ¡¢¤¨®¬«®±²±®«©§¥Ÿ•˜˜—‡}€„Œ•£®´µµ·µ¦£ œ™› «³¹¾¿¾½¼¹²ª§¢¹·³¯¬®®±·®¢Ÿš˜––”’‘’“‘†|{{zurplkifde`XQVlw}„‘ ¦§§©¬²¸¿ÆÊÍÐÐÐÎÌËËÌÊÈÆÄÃÂÀÁÁÀÀÂÃÄÅÇÇÆÇÆÆÇÉÊÉÉÊÊÉÉÈÉÊÊÌÌËÊÊÉÈÅÃÄÅÅÆÅÆÅÄÂÁ¿¼½¼ºº»»¼¿¿½ººººº¹¹¹··¶·µ³´³²³³³´¶·®•nO::GdЧ¶¸¹¹º»½ÀÂÂÆ¯EŠÏÆÆÆÄÁ¿¿¼¹·²¯««®±¶»ÂÆÈÉÉÉÉÉÊËÉÊËÊËÉÇÅÇÅÆÅÄÆÇÈÇÇÇÈÉÊÊÉÉÇÆÃÁ¿¼¹¶²®«¦¡Ÿ›™˜˜šœŸ£¤£¤«¯¯¬«¬¯°²±¬ª©¨£›“˜—’}…ˆ‘œ¨³¶³·¶²¬¨£žš˜ž¦°¶»¿À¾¼»·±¬§¥»º·²¯°®¬«°¶§ ›š—”•”‘Œ‘’“Žz{zxuromlgdaa]XRPgv{ŽŸ¤¥¤¤¦¬°¸ÀÇÍÏÍÍËÉÈÈÇÆÆÃÀÀÀÁÀÁ¿ÀÁÁÃÄÅÅÆÇÆÈÈÊÈÇÇÈÊÉÊÉÈÉÉËÌÊÊÌËÉÅÄÆÆÆÆÅÅÄÂÂÁ¾¼¼»¼»º»»½½¼»¼º¹»¹¹¸¶µ¶µµµµ´´´´´µ¶¶¸»¼¯—wP;2;X{˜¯¼ÃÂÂÁ¾É{H¶ËÈÇÆÄÃÀ½º¶²²±¯®®³µºÀÄÇÈÉÊÉÈÈÉÉÉÊÊÊÊÈÆÅÆÇÇÇÈÇÉÈÇÆÆÈÉÉÉÈÇÆÄÂÀ½¸¶µ±®ª¥¡˜–——šœž¡¥¤§«¬©«®°±±¯«««ª¨¢˜“•”…|‚†˜¤´³µ¸¶°¨¤š™›¤¬³¹¾ÀÀ½»ºµ¯«¥£»¹ººµ°¬©§§¯²¢›–““’’ŒŒ‹Ž‘“‘Œƒ|xwuroojfca`\XQK^sx}ŠŸ¡¤£¢¤¤¥¬²¹ÁÅÅÄÃÀÀÀÁÁÁÀ¿½½¿¿¿¾¾¿ÀÁÂÂÀÃÄÅÈÈÈÇÇÆÇÈÈÉÉÉÈÉËÊÊÊËÌËÈÆÆÈÈÆÄÅÄÃÂÁ¿¾¾½½½»º»½¾¼¼½»¹¹¹·µ´³µµµµµ¶µ´µ¶¶··¸··º¿À· bJ:<GZoŒ£®·œC}ÐÈÈÆÅÅ¿º·²±²³²°¯³¸½ÃÇÈÉÈÉÈÈÈÇÇÆÈÈÈÈÇÆÆÇÈÇÆÈÇÇÇÈÇÇÇÇÈÇÇÆÄ¿¼¹¸²°«¨¤Ÿ›˜––—›› ¤¦©¬¨ª¬¯¯±±«««ª¨ –”’Œz~„Š”ª³³³µµ°©£™–𣩱¸¾¿¿½¼»¹³¬©¥¡¿º·¶³®«§¦¥¥¯°—”‘ŽŒ‹Š‹Š‘‰~yurqqnjeb`]WRLKYouz‹Ÿ¡¢£¢¢¡£¥©¯´·»¼¸·¸¹º»»½¼º¼¼¼¼¾¿¿ÀÁÁÁ¿ÀÂÂÆÈÇÆÈÆÇÈÈÈÉÉÈÉÌËËÌËÌÌÉÇÅÇÈÈÇÇÆÅÄÂÁÁÀ¿½½¼¹»»¹ºº¹···¸·µ²²²²µ·¸¸µµ¶·¶¶··¸·º¼¼¾Á¿³˜ƒoYIABGVLlÅÎÉÇÆÄ¿»¸µ´´µ³²®¯µ»ÁÅÉÊÊÉÉÈÈÈÈÇÅÆÇÇÇÈÈÉÈÇÇÇÇÇÇÈÇÇÈÉÈÇÆÅÂÀÀ¼¸¶³±«¨§¦£Ÿ™——™š›œž£¦ª¬«¬®¯¬«ªª¨¦Ÿ–“Ž}zˆ‘™¡¬²³·´°¨¡ ›˜˜Ÿ¦®µº¼½¾½º¹µ±¬¨¤¥¾»·®©¨§¦Ÿ ¤¯ªš•’‘‹Š‰‹Š‹‹ŒŒ†€{vqoomida^[SLIGWqtw‡ ¤¢¡ ¡£¨ª«±±°°°°²µ·¹¸¹»º»¼¾¾ÀÁÁÂÁÁÂÂÃÅÆÇÈÆÆÉÉÉÉÈÉÈÈËËËÌÍÌËÊÊÊÈÈÈÇÈÇÆÅÃÂÂÂÁ¾¼»ºººº¹º¸¶µµµ¶´´´µ·¹º»»¸¸¹¹¸·¸¸¸¹¼¼¼¼¼¾ÀÂÅÉǼ³ž„v|™ÆÎÊÊÈÆÄÀ»·±±µ¶¶µ²®±¸½ÄÈÊÊÌÉÇÅÅÅÃÄÃÂÄÅÄÄÄÆÆÆÇÈÇÈÈÈÇÈÉÈÈÅÿ¿¼¸¶²°¨§¦¤¡ ™—™›œœž¡¦©¬¬®®°®¬¬ªª©¦¥¡“„wz€‹”ž§±µ¶³«¢Ÿ›™šŸ¤«²¶¸¼¾½¼»¸³¯©£¡¨¸º¹¯¢ ž•—›œŸ¯™•““ŒŒ‹ˆ‰ˆ‡…‚{xtonliea]ZOIDCOptwƒœ££¢ŸžŸ ¡¢¢¨«¬¬¯²³µ¸¹¸¸»½½ÀÁÁÂÂÂÂÃÃÄÄÅÇÇÇÆÈÉÉÉÉÈÈÉÊÍÍÎÍÊÊÌÌËÉÈÈÈÈÈÈÆÅÆÅÃÂÁ¿¾¾½½»¹·¶µµµµ·¸¸»½¾À¿¾¼»¼»ººººº»½½¾½¾¾¿ÀÂÃÂÁÅÇÇÄÈÍËÊËÉÈÆÁºµ°°°±´µµ³³¶»ÀÆÈÉÉÇÄÃÁ¿¿½»º»½¾¿¿¿ÁÂÃÆÅÆÆÇÇÆÅÅÅÄÂÂÁ¿¿»·µ±ª§¥¤¢ Ÿ›˜—™›› ¢¢§ª¬®¯¯°°¯¬«©¨§¤“ŒywzŽ˜¤¬¯²µ´¯§ œ››¤ª¯³·º¿¿¾¼»¹´¬¦¡£«³´³³©–’””’•°ž“”‘Œ‹‰‡‡ˆ†„~{xtqolhec]UMHA@Iluxƒœ¤¢¡ž››ž ¢¡Ÿ ¥¨ª««¬®°³µ´´¶·¸º»½¿¿ÀÁÂÃÃÃÄÃÄÅÅÇÆÇÈÉÈÊÊÊÊËÍÍÎÌËËËÌÌÊÉÉÈÈÈÇÇÇÇÆÆÆÇÃÀÁÀ¿»¸¹·¶µ·º½ÀÁÂÃÃÃÄÂÀ¾¾¿½½½½¾½½¾¿¿¿¿¿¿ÁÂÁÂÀ¿ÀÄÇÉÊËÉÆÄÀ»¶°®®¯±²³µ¹¿ÆÈÈÆÅÄÁ½»º¹·¶³³¶¸ºº»»¾ÀÂÃÄÅÆÅÄÃÃÃÃÃÂÀ¾º¸¶µ°«¨¥££¡ž›™™œ››› £¤©®°®®®«ªªª¨§£›‘…uw}‰’ž¦¬±´²°ª£Ÿœœž¥ª¯´¶»¾¾½½»º¶°«¦¤§°©©ª²¯£Œ‹Ž‹Ž’š¨µ¦˜’‘ŽŒŠˆ‰ˆ‡‡‚zvtplifcaZRJE=9@gvz…¤¢žŸ›žŸ ž £¦¨©©«¬¬¯²³³´µ·¸¸º»½½½¿ÀÁÂÃÄÃÄÅÆÇÇÇÈÈÇÈÉÊÌÌÎÎÎÌÌËÌÍÍÌËËËÊÊÈÇÇÇÇÇÈÈÆÄÄÄÿ»º¹º¹¹¼¿ÃÇÆÈÇÇÆÄÄÂÁÀÀ¿ÀÀ¿¾¾¿¾¿ÁÁ¿ÂÄÄÄÃÂÃÆÉËÌÊÈÄÁ½»·³°®ª«®¬®³·½ÁÇÆÄÂÂÀ»¹·¶¶µ±¯°²²µ¶·¸»½¾¿ÀÁÃÄÂÂÃÃÃÁ¾¼º¸µ³°¬§¤¤¡ Ÿžš™™œœ›¡¡£¥ª®¬®««ª¨§§§¥¡švtz‚™¢ª¯²²¯¬§¤¡ ¢©®³¶¹»¾¾½¼»º¸´°ª§§®´¢¢¤§©§™‹…†‡‡‡Š“£°ªŸ•‘ŽŒ‹ŠŠ†„|uqmkida]WOD9407cz|…— ¢žœš›žžœ¡££¥§¦¨«¬°²³´µ¶·¸¸¹»¼¼¾¾¾¿ÀÃÄÄÅÅÆÇÇÇÆÇÇÈÉËÌÏÐÏÎÎÌÍÍÎÎÍÍÎÍËÊÉÉÈÈÉÈÉÈÆÆÇÄÁÀ½»¼¼¼¾ÁÅÆÊÊÊÊÉÇÅÄÃÂÁ¿¿À¿¿À¾¿ÂÁÀÀÃÄÄÃÄÄÅÈÊÊÉÇÃÀ¾»·´²²°®¯¯®±µºÀÄÅÄÃÀ¾º¶´³±²°¯±²³µ¶·¸¹»¼¾ÁÂÁÁÁÀ¾»¹¸¶³°¬¨¦¤¤¢ œ›™™›››Ÿ¢¤¦¨ª«¬¬¬¬¬«¨§¥¤¢Ÿ—ow|ˆ’§²±®©¨¦¦¨¬³¶¹º»¼¾½½¼º¹µ²¬§¥¬³µ›œžŸ¤¤‚€€„„†˜¨¨ž”’ŽŠ‹‡ƒ~ztplkeb\YQD:41.1Xx}†– ¢ ›šš›žž ¡¢£¤£¦ª¬¯²³´µµ¶¶·¸º¼¾¿¿¾¿ÀÂÄÄÄÄÄÆÇÇÆÇÈÈÊËÌÏÑÐÏÎÍÍÏÐÏÏÎÍÎËËÊÊÉÊÊÊÊÉÉÈÇÅÁÁÀ¿ÀÁÀÂÃÆÈËÊÊÉÆÅÃÂÀÀ¿¿¿¿¿ÀÁÀÀÀÀ¿ÀÃÃÂÁÄÆÆÉÊÉÈÄÀ¾º¹·¶µµµ´²²°°´·»ÀÄÅÄÃÀ»¶±°¬ª«¬©§¥§©¬¬®°²²´µ¶¸¸»¾¿¿¾½¼¹¶µ²¯¬¨¦¤¤¤¢ššš›œœ›œ ¢¥¨ªª¬¬®¬¬«©§¨¥¡Ÿ‘wty‚Œ–¤«¯¯¬¬«ªª¬°µ¶º½½¼½»»½»¸¶³°©¥§²´–”“‘•˜ €~{}~€„‡‘ ±¦•Ї|vsojfb\VME?;620/Jv}†”Ÿ¢ ››˜˜›ž ¢£¤£¥¦¨ªª®²²´¶¶·¶¶·¹½ÀÁÁÂÃÄÄÄÅÅÄÅÆÆÆÇÈÊÌÌÍÎÐÑÑÎÍÏÑÑÐÐÎÍÌÌÌËÊÊËËÊÈÉÊÊÊÈÆÅÅÃÃÅÇÇÉÉËËÉÈÆÃÁ¿¾¿À¿¿¿¾¿¿ÀÀÁÀÁÁÃÄÃÃÃÆÈÉÊÊÉÆÂ¾½¹¶µ´´´µ¶¸¶³³´¶»¿ÂÁ¿½¸³°«¨¥¥¥¤£ ¤¦¨¨©««®±´´µµ·º»ºººº·µ´°¬©¦¥£¢¡žœ™˜˜œž ¡¥©«ª«¬®¬¬©¨§§¤ žsu{…ž¨®¯¬¬¬¬®²¸»½¾¾½¼¼»¼¼º¹¹³«¦¦«°±²™Š‹Œ™•…~{{zz{~…˜¤°¯©“‰„}vrmgb^ZQIHDB?9736Dk}…šžŸœš——šœžŸ ¢¤¤¥¥¥¤¦ª¬¯¯±±²´´³³µ·»¿ÁÂÃÄÄÄÅÅÄÄÄÆÆÆÈÉËÍÍÎÐÑÑÏÎÐÐÐÑÏÍÌÌÌËÊÊËÍÎÌÊÊÌËÌËÊÊÉÇÈÉÊÉÉËÌÊÉÇÄÁ¿¾½¾¿¿¿¾½¾¾¿¿¾¿ÀÂÃÄÅÇÇÊÊÉÉÇÅ¿»¹·´²°®°²´´³´µ¶¹¼»·µ±§£¤¤¡ ž›œžŸŸ¢¥¤¦¦¦©«®®°±´µ¶¸¹·¶µ´±®©§¦¤¡žœœ›˜——šœžžžŸ¢¥©«««¬¬¬¬ª¨¦¥¤¢Ÿ›‡twŠ—£«®¬«ª¬«ª®³¸¼½½¼½»º»½½¼¼¹¶¬¦¦©°±±–މˆ‰‰Š–„|yxyz{|}ˆœ¦«®¦“upe`\WOIGHGCA@=;:Bdz‚Š˜Ÿœ›™——šœžŸ Ÿ¡£¤§§§©¨©¬¯¯²²²³´¶¶º½½¿ÁÂÃÃÃÃÃÄÅÆÈÇÇÉËÎÐÏÐÐÏÏÐÑÑÐÏÐÊÂÊÎÍÎÏÎÏÎÌÍÍÍÌËÌËÈÉËËËÊÉÊÊÉÈÄÁ¿½½¼¾½¿¿½»»¼¼¼¼¾ÁÃÃÄÆÈÉËÊÉÇÿº··´±®¬««ª«®¯³´¶·´ª¥˜–˜™˜•”‘Ž’˜š›ž £££¥¦§ª«¬¯±³µµµµ³²±®ª¦¤£¡Ÿšš™™™˜™žž ¢¤§ªª««¬¬«ª¬«ª¨¥¤¤£Ÿ›‡t}„ž¨¬¬¬ª©©©©®´¸º»ººº»ºº¼¼»»¶¯¦¥¥¦«®¯°“‹‡„„…ƒˆˆ}yxwvvxy{„‡—Ÿ¬®³®£‘}k_VOMIJIGEBA@=?Yr}ˆ–ŸŸš–˜˜˜˜–šžžœœ £¦¨ª«¨§©«¬®°±°±²´´µµ¸»½¿ÁÁÃÂÄÅÆÆÆÈÇÇÈÊÌÏÑÐÐÏÎÏÏÏÏÐÕ»]_lw‡²ÑÏÏÎÎÍÍÍÌÊÉÈÌÎÍÎÍËÊÊÉÆÁ¿½½¾ÀÀÁÀ¿¿¾¼¼¼»»¿ÁÃÃÄÆÇÈÈÈÆÄÁÀ»¸¸¶´±©¨§¥¥¤¤©¯±±¬¦¢–‘ŽŒ‹Š‹ˆˆ‹•˜š ¢£¦¨ª««¯²´´´¶µ²°«¨¤£¢Ÿœ™šš›š™™›ž ¡¥¨©«¬«ª«ª«©ª¨¥¤£ ™„y€š¨««ªª¨¨§¨ª®²´µ¶···º¹¹¹»º¶®©¥¤¤¥¨¬«¬”Œ†‚‚‚…Š…}wusvxxxz~‚‡ˆ“œ£ª°³µ¯¥œ‹xoeZRPIFDBPlz†‘žœ•”••”’•—››šŸ£¦©ª¨©¨¨§ª®¯®¯±²´µ¶¹¼¾¾ÀÂÂÃÅÆÆÆÆÇÈÈÊÍÎÐÒÑÐÏÎÎÏÎÎÎÒ«:8752†ÖÏÍÍÎÎÎÍÊÈÉÈ«¨°´µ·¸ÄÈÄ¿ÁÄÅÅÄÁÁÁ¾½½¼¼¼¼½¾¿ÀÂÅÄÆÉÌËÇý´²¯©©§¡››Ÿ¡£¡¡¤§¨¨ª¤žœ—‘ŠˆŒŠŠ„„Š“–™›¢¥¦ª®¯±µµ¶¶·¶²¯ª¨¦¤¡¡žœ›™™š›œ›œž Ÿ ¥§¨©ª««¨¨¨¨§§©¨¥¤¡ž›–z†”¥¬«ªª©©©¨ª«¬±²±´´³³¶¸ºº»¹²¬¦£¢£§««©«•‹‡ƒ~€€‚‚~xurssutuxy{{~‚‡‹’š ¦°³±±°¬¥Ž}jYOKPhuƒšœ›—‘’‘‘””–™™›Ÿ¤¨¨¨¨¨§¦¨©ªªª®°³µ·¹»½½¿ÀÁÁÃÅÆÇÅÆÇÈÉÍÏÑÑÐÐÏÎÎÏÏÏÎÓ™:?>?:–ÖÌÍÍÍÌÍÊÊÒºl><ABDWÂÆÈ‹exŸ¯¹ÁÅÇÃÀÀ¾½¿ÁÀÂÂÁÂÞœ†qbUHD?866732489<>VŸ¢†IFHLMKMLOT]ds‹‹‘—š›Ÿ¢¥§ª¬¯±´¶¸¹··¸´¯¬¨¥£¡ žœšššš›››œœžž¡£¤¦¦¦¨©©¨§§¦¦¥¥¥¤¢Ÿ›’|{¡ª©ª««¬¬«ª««¯¯¯°²·¹¸·³¨¥¡¡¢¦©©©«™Š†„}z{}~€ztqppqrrsvuxxz~€‚‚ˆ•˜›œž žŸ™‚s_RQaq~Œ”˜›—’“•––˜™ž£¤¤¥¥££¥¥¦¥§«°²µ¸¹»»»¼¿¿ÁÃÄÅÆÆÇÇÇÈÌÏÒÒÑÐÏÎÏÏÐÐÏц;C>>>¦ÐËËËËÊÊÎÊ•E-:<=9QžÎÈÁÆi'..4>IVfs”˜››’‹‰¹ÄÃÉ—/*'&(&'$"!!6›š™¡a!9q•›œž £¥§«®²µ·¹»º¹¸µ°«¨¦£¡ŸŸ›››œœœ›žŸ¡£¤¥¥¥¦¥¦©§¥¥¦¥¤£¤ Ÿœ˜ƒ™©¨©ª¬«ª«ªªª©©«««ª¬°³³²¯«©¦£ŸŸ¢¥¦¨©©•‹‡ƒ|xxz{}~~ytpopppqrsttvwwx{}}~‚ˆ††‡ˆ‰€vfVO[n‡’–—™–‘ŽŽ””••”–šžžŸŸž ¡¢¢¢£¦¨ª°²¶¸¸¹º¼¾ÁÂÄÅÅÆÇÆÆÈËÏÑÑÐÑÏÏÏÏÏÎÍÐw:DA?@ÎÊÊÊÉÌÒ¯c.3<=?:h¸ÑÉÄÀÄc243310.--/1/1552215¨ÇÂɈ140.,*(''&#""!! 9˜™–›Z " EŽª£§©«®¯±²³·¸¸¹¸·µ°¨¥£¡¡Ÿš™šš›œš›œœŸ¡¤¤¥§¨¨§§¦¥¥£££¤¦¥¥¥¡Ÿš•€’¦¥§©ª«ª¨¨§¨¦¤¦§§¦¦¥¨ª¬ª¨©¨£žœœŸ£¦¨©ªš—†€~|ywxyy{}{uqmlmmmnooppopqsrsvvrutqqqrrnnj_UKTl‡””•––’ŽŽ‘’’’”——™™› ¡£¥¥¦¨©¬¯³µ·º¼¿ÁÅÅÄÅÆÈÉÊÍÐÑÑÏÐÎÎÏÎÎÌËËk;B@=C´ÌÈÊËÑË„80:=>9B…ÈÑÇÆÃÀÆ^43225689:9877778:<@®ÄÂÊ~.1/+*)'%$#!"! "! @™˜™N!#$%-†¯ª°±³µ´µ¶¹¹¹¸¶²©¦¢¡¡žœšš›š››™—˜™œ ¢¥§¨©¨§§§§¦¦¤¢£¤¥¤¤¤ ™˜¡¤¦§©©¦¡ ž™––™ ¡¢£¢£¤¦¨§§§§¨¦¡œ™šž¡£§¨¨‘“‘ˆ€|zzwuuvxy{xsmjiiiiikjllijjjijljhggfeeecb]WMIMe{ˆ˜•““‘‘ŽŽ‘Ž‹’••–™šœž ¢¢¤¢¢¥¨ª°²´·º¼¿ÁÂÃÅÆÈÉÌÐÐÑÑÏÎÌÍÎÍÌÊËÇ[DC@9I»ÊÅÅγc.4=@<9V£ÐÍÆÇÇÃÁÃU.123242024467:9797=¯ÀÁÉs.1-*%*71/.,)(#" HŸ™”’C(%#"!%$&($.”³°³´µ·¶¸¹º»»¹µ°«§¥£¢¡ž›š››œœœ›˜˜™ ¢¤§¨©©§§¦¤¤¥£¢££¢¢¢¢Ÿ›š—’™¡¤¥¦¥¢œ—‘‰€~€‡˜žŸ¢£¡¡¦©©¨¥ ›˜˜›¢¥¤¤¦_ˆ‡~zyxurstwwxtlifeecccedbcc`b``ac``_^^\ZYXTPF@I_x„˜—”’‘”’Ž‹ŽŒ’’’”•™›ž ¢¡ ¡¤¦ª¬®²µ¶¶¹¾ÀÃÄÄÆÇÊÏÐÑÐÎÍÌÌÌÌÉÈʽL<@@8SÀÃÃÈ”D3::;:;t½ÎÇÆÅÅÄÁÀ½K034.B€yl\TJ:788669Z·½¾Åi10++&v “•“Œ…{tmffdx•“ŽŽ@(x†‚~vY3"$(*-&A«·¶·¸¸¹»»ºº¹¶±©¨¥¤£¡ žœœœžžžœ›šœŸ¢£¦¨ª«ª¨¦¥¤£¢¡¡¤£¢¡ Ÿœš—”“𡣦¤Ÿ™‡ƒyuuu‡’™›œ ¤££¨«¬«¨£Ÿš–™Ÿ¡¡ £¤4^†……‚}{zwtqpqsutmifa`^^][XY[WVTWWWXYXYWUSQOMID;34]w‹“——”’’‘ŽŒŽŒŠŒŒŒ‘’’•–—™Ÿ¡¡Ÿ ž ¡¡£§ª®±³µ¸»¼¿ÃÅÅÄÇÌÎÏÎÍÌËËÊÈÇÆÈ³@48:6U½Â¾s24<881DËÇÀÀÂÁÁÀÀÀ·B256/_ËÇÉÄÀ·¦—”˜©¸¾½¿Äb)-+++‘²©§§¥¢¡Ÿ¡¡žžœ˜“‹47 ¡¤§«®›Q&*,,.'{¾¹»º¸º¼»»¹·²°«¥¦¤¥£¡¡žžž ŸŸŸœœž ¤¥§©ª«ª¨§¦§¦£¡¡£ ¡ œœš–”•› ¥¦£Ÿ•Šytrtv}†˜œ¡¦©ª¬±²±®ª¦£Ÿ¢¥¤£¢¢¢@5`†ƒ|{vwurooqrtqifc`^\XVURQPMNOOPRRPPOLKID@7,!T}‚ˆ•–”“‘’’‘‰‡‡Š‘“”–˜—šœœžžžž ¤¨«°µ·º¼¾ÂÄÆÆÈÊÍÎÌËÊÈÇÅÃÂÂÄ©93540O¾°V-68760S¤ÆÁ¾½½¾¾¾¿Àó?3451jÈ¿ÀÂÃÃÄÅÌÎÉÆÃÁ¿À¿Â_,.,()’«¤¡Ÿž›š›˜˜˜—•“,= ¡£¥§³«P(..0,O¶»»»»½¼ºº¸µ²¯«¥¤££¡ ¡œžŸ ŸœœœŸ¤¨¦¦§©ª©§¨¦¦¤¡ ¡ Ÿžœ›™˜–“”›¡¥£š“‡{sqnnpuz‡’›¡¥¨ª®°±¯®ª§¤Ÿ¡¢£¥§§¥¥D@<^ƒ~~{xwutppprrqoida][YVSPPMHFECDIHHGEB@:0!P|„‡Ž“•–•“”––”‘ŽŒŠ‡†ˆŽ‘“–––˜™™™˜™š››œž ¤¨«®´·¹¿ÃÅÅÆÈÌÌËËÆÃÀ½ºº¹¸»™-./.(LŽA&53350d²Ãº¼¼»»¼¼¼¾¾Á¬>4352nÆ¿ÀÀ¿ÀÀÃÄÃÂÀ¿À¿¿½½X++)&*‘¦¡Ÿœ›————˜š™”’†'F¥Ÿ£¥¨©«¸œ6,/12<¤À¼½¼½¾¾¼¹¶³®©¥£¢ ŸŸžžžžŸŸŸŸŸ›œ¢¨¨¨¨©ª¨¨§¦¦¥¢ Ÿ ¡Ÿœš—•–”’”šš‘ƒ{qnnlmmptx{ƒŽ˜ ¡£§ª««ª¥£¢ žœžŸ¢¥¤¤£DCA8f||{zutsqnnnmnnjf`][XVRQMKHD>;99<=:3+'
Hy‚†Š“”””•””“‘ŽŽ‹‰ˆ‡ˆ‘’‘’”“’”––——˜™š›œž¤§ª®°¶¼¿ÀÂÆÈÌÌÉÆ¿¸²®®®¯°µŽ%)'()+."-.-,1l·º´¶¸¸¹»¼½½½½À§84493qĽ¿ÁÀ¿¾¿ÁÁÂÀ¿¿¾½»ºQ%'%#'ŠŸ›™šœ›–•—™˜˜•‘Ž€#M§¢§©«¬®²·Q*//34“Á¼¾¾¿¿À¾¹¶°ª¦¤£¢ žžŸ Ÿž ¥¨§©ªªªª¨¥¥¤¢¢ŸŸ Ÿœš™˜•“‘”—“…}tllmjmmptsux‰“™™œ £¢¢›‘‘—œ›™š› ¢¢¢BCA;=jxwyvtrromllljjhb]ZXUSPMJGB;7542(
D|ƒ„ˆ“•–—•’‘ŽŒŠ‹Œ‹ŠŠ‹ŽŽŒŽŽŽ’““““•—˜™˜œž¢¤§ª±¶¸»ÀÃÅÉÊÅ¿µ¥¡¡¡£¦¯€!&%%%$%))++.y¹¸±²³¶¸¸¸»½»¹ºÀŸ53242tȾ»¼ºº»¾½¼¾¾¼»»º¸ºM%&#"%‡˜–•—–•––“’’”Œ‹Žw\ª¤©®®²´»_(1263–ýÀÀ¿¼¹·´±¬§¤£¢¡¡ŸžŸ ¢¢¡ Ÿœœž ¤§¨§©«©©¨¦¤¤£¡žž Ÿœ›™˜—•“‘ƒurkijkloqqtstx}†Ž“˜™š˜ƒ||ˆ•˜––˜šž¡¡¢CCC@7?kuvxurqnlkkjjlhc^ZWTRPMIE@<851(
=|‚……ˆŽ”•–•”‘Œ‹Š‡‰Ž‹‰‰ŠŠ‰‰ˆ‰Œ’’‘””“’–™œ £¨®±¶¹¾ÁÄý¶ª¡œ˜˜™›Ÿªp!!!!"$%&&%T¶¯¯°²³µ···¸¶¶¶½‘/100.R˜¤®·º¼ÀÀ¿½¼»¼¹¸··¶H"$ #‰¢›œ™–’‘”•’‘‘ŠŠŒm e¯§ª¯°±¶¹¿Y/0588¤ÅÁÀ¾½·´²°«¦¡¡¡¢¡Ÿ ¡¡¢£¢¡ žŸ¢¦§¨§©©§¦¤£££¢Ÿœž ž››š˜–”’މ„tolkiiklnqrsusx~ƒ‰ŒŽ’‘‹€tpqŽ““•–šœžžœCBCA=8>outtrqmmlijhiheaZURRQMID>975(
8x}ƒ„†ŠŠ”•“‘ŽŠŠŠŠ‰ŒŒ‹‰‡†…„„ƒ†‰‹‹ŽŽŽŽ’–›¡¦©±³¶¹´±© ›—•••˜›¤h# ""$$3›®©®¯¯¯¯¯°³´´²»„*--,,'(1>Lau…˜Ÿ¡ §¼¸´±E#!FTSYYZ`dagigkpŠ–c " p±«°³¶¶·¼¬A6346F·ÄÁ¾¼º¶´³®¨¢¡ŸŸŸžžŸ¡¡¢¢¡¢ Ÿ ¤¤¦¦¥¦§§¦¥¢¢£¢¢ š››˜•”“‹€{vqnkihhkmnqrqtv{‚„‡‰Œ…wpor|‹’’•–™š›š–AA@@>=5Bqrrtqnmkifffffc]WSROMHB=74*
0r|‚ƒ…ˆˆ’”ŽŒ‹‰‰……‰‹Š‹Š†ƒ€€€ƒ„…†ˆ‰‡ŠŠ‰ˆ‰ˆŠ–˜œŸ¢¦¨««©¥™–”‘”––—ž`5 !";ž§¤¦§¦¥§¨©©®¬µt$,+)*+(('&$'/028:;5„Áµ±«? @“’“›_ "&"z´¯´·¸¸»Ãl59573tÄ¿¿½»¹¶µ²¦¡Ÿ ž›œŸ ¡¢¡ ¡¢¤¤¥¥¥¥¥¥¤¤¢¢¡ žš˜˜š˜“‘Ž‹|trqmlighmlnpqruwz}€ƒ‡ˆytomrx~ˆ‘”˜š›™—‘@@@?<=;1Jpprqnmkifcacca`[UUPMFB<9+
3m|…„†ˆ‰Š‹ŠŒ‹Š…‚„„ƒ‚€€}}}||~~€€‚ƒ€ƒ‚€ƒ‰Ž’–˜™ £¤¤¡œ—•““–––”šQeTM£››Ÿ¡¡¢¥¦¦§§§e))(*+,+,./25446872‰À³§>Dš˜š¡^##&$µ°µ¶¸½Å~/9875=®Á½½¼»¹·´°©¤ŸžžœœœœœŸŸŸ ¢¢¡¡£¤£¢¢¤¤¤£¢ žžœœœ™˜˜–—”‘ˆzupljifehkkmooqtwz}~€}yslknv|€†‹”˜››š“Š???>=:75,Nnqppmjgedaa_]^[XQNHB@8$ +
)jx€‚„†ˆŒŒ‹ŒŒŒŒŠˆ„~~|xwz||}|{z{{{z|}~{z{z{|~€ƒˆ‘“–™Ÿ Ÿ™—“‘“–˜•–Io˜2j¡–šœ™ ¢¡£¢£U%%'$)'%'+,.0214785޼°¨£8 GŸž¨\#&%(&‡Àº¿À¿ªj/7;:82ŠÇ½¾½»¹¶³®©¤Ÿœ›››››œ›œŸžž ¡¢£££¡ ¡¢¤¤£¡ Ÿœ›žŸš••••“‘ŽŽŽ…ytpmhfeeeilmnpswz~}}|zvvnikrx~~‚Š•›ž›–Ž…?>>=<:85/)Kpllkihfcb_][ZXUOIE@.
+
%ct{ƒ…†‰‹‹Š‹‹‡…‚€|{zxuwxzywyxvwxwwxxuttuwx||{}€ƒ‡Œ“—›œ—–’Ž‹Œ”–‘Cq™~ x™’”–š››œžŸ¦F!"#)~ŠveTJB:965767’·©£›0K£ ¤¨V$&&)#^ˆƒzs[:+49=<1xƽ¼½»·³°¬¦¢Ÿœšš™š›š›œžŸžŸ¡ŸŸ¡¢ žž¡¢¡ ŸœŸžœš—‘‘“‘Ž‹ŒŠ‚vqmlifdcehkmnsy}yxvvtpghmrx||€Š•šœ•Š‚?==;;8641-*Gjkgffdcc_]YVRLLE,
+ +
%_ryƒ„ˆˆŠ‹‰Šˆ‰ˆ„ƒ‚}{zyxvwxwvuvvttttttqorsstwwwww{~…‰•—›š—”ŒŒ’–‘:m‹‘g&„Œ’”•–˜˜™›œ7!! 2¦¸¹»º¸²«¦¢œ™––¨¬¡š+*UTTQONKIHIIKJrª¤¤§N$''))&))%(*/34:6:}Á¿¾¼¼¸³¯«¦¢ž›šš™™š™››œžžž žžŸŸ ¢¢ Ÿžžœ›œ™–”’’‘ŒŒŠ‹‰spmkidcdfiilqz‚…ƒ}wuusrqkchoqv|€†”›ž—’†><;;:54411/):_gfb`ab_ZUMKI:!
+
Wrw}‚……‚†‡„‡ˆˆˆ†„€|yzzvwvuutvtssrsrqompqptutwwvwy€‡ŒŽ–›™•“Ž‘Œ6n‡…‰J6Œ’“”—˜—‘.7Ÿ¦«²µºº¼¾½¿¿¾º±¥š“‚%C”‘‘”••—œ£¨§¥¤ªL$((*,-020234213PŸÊÀ¿¾½º³°¬¨¤ œš˜™˜™™™™šš›š›ššššœ›œž ¢¡¡ Ÿž››››š™–•“‘ŽŠ‹‹Š‰‡~vtpnhcbegklqw„ƒxvtqonlhdhnow‡’ ž™ˆƒ{><::85434311,1Oa^\\ZVPNL7
+ +
Lqx|‚ƒ‚‚‚„…ˆŠˆ‡‡‚{{ywwvtuutttrqsrrpooqprrqssttx|†‡–—–“‘‡‡†ˆ‰‰6o~|}‚4G‹ˆŠŽ‘’’”‡%:˜š¢§¬°²³³³´²°¬¥šŒw J‹ˆ‰Œ‘’–—™œ¡§¨ª©¬M&++-/-+.,-1/?b‘ÁÌÂÁ¿»¸±®¬¨¤¡žš˜—˜—–“’“””’‘••”’’–—–™¢¡Ÿš˜—˜˜–“““’‘‹ˆˆˆ‰ˆˆ„}|ysnidbdhmu{ƒˆ„€yuuspolhdefimw…“›¡¡Ÿ™”ƒw<;9877655331/.)?ORRQLIA"
+
<t{}‚ƒƒƒƒƒ†‰‰ˆˆŠ„‚~~{yvussssssqqrpqqpnqpqpopqrtwz}„‰‘••’‹†„„††‡5"w€{{|t#b‹„†Š‹Ž|<”’”•𠤣££¡Ÿš“Žˆ†jT‰†Š”–˜œ ¥¨ª¬¯±M)/164>LVas°ÂÉÅÁÁ¿¹·´¯¬©¥ œš•“‘‘ŒŠˆˆŠ‰ŒŽŽ‘Ž‘•–˜˜••–“’‘”“Ž‘’Œˆ‡‡††‡ƒmtxslfcaensy†ˆ†{tpqonkecfghio‚“¡Ÿ›•ˆ‚|x;::877442320.--3?A@EG5
+
*p{~ƒ„„„……†††††ˆˆ…~€€}{wvtqrrqrnqqqpnmnoppponpqruxz~†Ž’“’‹ˆ„‚„„/"uƒ‚~{€fp‡ƒ……ƒ‡t>‹ˆ‰‰‹ŽŽ‘’Šˆ„€€…a]‡†‹ŒŽ•–™Ÿ¥«¯±³¶Q-16;<ŸÁÄÉÌÍÏËÅÃÿ¼¶²¯«¨¢™–’Ž‹Šˆ‡ˆ†…„ƒ„„ƒ‚‚…†„ƒƒ„†ŠŒ‹Š‹Œ‹‹‹‹‰†„„ƒƒ„‚aanmjeabhmu|‚„…‚{tqoomlhb`cgiiju„”š—”Žˆ„‚zw97788842310/,0;A@;8=4
+
$c{€‚„„„…‡††‰‰†…‡…€€~}{xwurqsspnoooonnmnoooppqruuwz}‡Ž’’’Œ„})!wƒƒ€{Y(ƒ~€€~‚jDƒ~€ƒ‚ƒ‚‚€~}~‚Ueƒ†ŒŒ“–—ž¢¨±³µ´µQ2369H½ÍÉÊÉÇÇÇÆÄÿº·²®ª¦¡œ—‘Š…„€€€}}|}}}|zzzz{z|}|ƒ‚ƒ…†ˆŠ‰ŒŠ‡…†Š‰‡ƒ‚‚€~€‚€bYdhecabgkxƒ„|upnnmmjd__cfhhjmr|‡‹‹ˆ‚€}ww5676665332/,1BGDA:90
+
_}‚„……„‡ˆˆ‹ŒŒ‹‡†„‚€~}|{zwutttpopnoopqpprrsuvxwxy{{}‚‰’’‘‡v%&w€~~{y‚G=…~||z~_H…|}€}{||yxxz||}„Ks‹‹‘”˜›ž¥§«°´¸¹¸´J1344J¹ÆÇÈÉÈÈÈÆÃ¿»¶¯ª§¢œ˜”ˆ…ƒ~|zzzxwvxywvwvuuvtvyz{y{~~„…ˆ‰†ƒ‚ƒ‚~}}~~}`[`fccbcglw€ƒ‚|wsoonlke_^`ceggjkmsz‚…ƒzxz}578645542219EIGB>?:
+ +
Ly‚ƒƒ„„†…†‡‹‹‡……ƒ€~|{xvuusurqqqpqrqrtuy{~ƒƒƒ‚‚ƒ€„‹ŽŽŽŽ‰‚}{m )z}{{|}}~~4Mƒzzx}S.RV[bdhopsvv{~|€~„E"ƒ›—–›ž¢¤¨«±´¹¸¹¹»´I5693M¿ÆÈÊÉÈÇÆÃ¿º´¯§ š–Šˆ…€}|zxwvwvttutvtrrsrsqppqsuvxyyz|~€‚‚‚~€~~}zzyz}{`[`baabehox€~|xsnmlkhb^]beggeffimx€€}yw|ƒ7865533414BJIGE@<@
+
:x‚~}€€€ƒ„„……ƒƒ€}{zwxwuvuurrrqrru{€…‰Ž”“’“Ž‘‹‰‹‹„}yui"({zzy{|||v&^ƒxv{M#(+06:R~„=`twz†‰–˜š›žŸ±»¼À·H7776XÆÊËÉÈÆÃÀ½¸²¦ —’Œ…‚|{yvuttrrrqsttrqqpqooooooqrsuvuvxyx||||||~~|{zvvvvz|x`[___`bcis|€ztnmjigc^\`dfeggbglp{}}{vyˆ77534322>JPJIFD>2
+ +
.wƒ~zyz|}}€ƒ…„ƒ„†„ƒ}}}||yxwwvrrtvz†‘—𠢤¤¥¥¢žš—”Œˆ†ˆŠ…|wro_PJE>Gwxzy{yzxx|dl{rwF/€€5!$&)+.1147›ÂÀõF7887hÌÈÈÇÅý¹´±¬¥ž—”Žˆ€|ywwwtrsrsrqprrqponllmmopnopprsrrstuuwyxxxyyzxwvtuvuwzv]Z[]_`delu}‚‚|tojkhie]Z\bdgffhfms{~|{{vv|‡7553229JVTPJHE:/!
+
*f€|xxwz||~}~‚‚„†„…††…ƒ‚ƒ€{zyzyvy|~†Ž– §ª¬¯³±²µ´±¬©¢™’‰„ƒ…†…€ytoqsx{{wvwx{{|}|{z€V#&pvyB5€0"""%'(*+,//15ŸÆÁÇ®B:;<7rÍÈÆÃ¿½·²¬¦Ÿ™”Œ†€}zwvutssrrqrrqqpomlkjiklmmnmmooppppprrsttvusstuvuuututuwoZYZ^aceinv}€|vqniigfa[[aeejhhimu|€|}|zvz†”œ52123ASWTSKE<70,!
+ + +
"[y~zwtrswwyz|€€„††‡†ˆˆ……ƒ€{{{yz‚‰‘™¤±³¸¹»»»º¹¶´±¨œ„€‚„ƒ‚|wtrqprtsstuyz|}~}}|sjbXOI;[ww@A‚~ƒƒ. ""#&')*-/2359©ÇÂÇŸ76530…ÌÅľ»¶±¬¥ž™Ž†‚|yvutttrrrqqqppoomkjihhhghijijlmmmnopoqrqppqssrsrttstrsssukVW]`efimsy€yrnlgghe\Y\aefjkjnt|„…~{zvx‚‘œ 31/-3AA?A=75320/#
+ + + +U{{wrrqrrstyz}€ƒ„…ˆ‰ˆŠŠ‰‡‰‡…‚~~‚…‹“š¥³º»¼¼½¾½º¸µ±ª£˜Š~{|~€€}zyyvuttuuuvz|||~}|}}}|x{}|{xxoaZSKD<62,)$#I‚€†) "!#%')*+-33/<ÃÂÆ›S\bv‹µÇþº³®§¤ž•†~zvuttsrqppqppoommljhhghhfdghfggijllmmnnmnpqppqqrrpqrpqqrpqtqbadfhjntyƒ}wojigge^XY]cfjjlpsyƒ‰†}yut{‡“œ 1.,./.,.43334422'
+ + + + +
Lyxwtqpqrssty|„‡‡‡ŠŠ‰††††‡‰†„‚ˆ‹’›£«²·¹»»»¼½¼¹¶²«¢—Œ~{zz}ƒ‚‚~{zzzzy{zz|{{€‚|zzz{}|z€{zyzywxxwspnlhcYTQl…‰ƒ?2.2300.1656;@FGNSZv¾ÂÅÆÃÂÊÏÒÐÉþ¹´®ª —ކ‚|xvsrrqopooopoollklljjijmlifiigeffiklllmmnlnpomnopomoqponnnprqighjiovƒƒxqlihge`YX]cfijjmsw~…†€zvqs‹”›¡.,+-.-/043566753
+ + + + + + + +
Euttsrsssstux{‚†‡‡ˆˆ†‡ˆˆ…†ˆ‡………‹Ž— ©±¶¹»¼¼¾¾¼¹´°¨š‚zxyz}…………‚€~€~ƒ‚„„„‡…‡†‚‚‚€ƒƒ„„€|{xyz||~}~€‚‚ƒˆŽŽ‹ŠŒŽ‘““•™ž¡¤§¦©¬±¹¼¾ÁÆÍÊÈÉÉËËÈÉÇž·²ª£›‘‰„~yvtsrponmmmnonoomklnnnoorvurpmjihhfegjjilloonoononnmmlopnmlmmqpkijkoxˆ†{tnieeb`XV[_dghkmqw€‡ˆ€zwurx‚Œ”™ ,+,./00224677742
+ + + + + + +
:wwvvuvttuvwxy~ƒ„ˆŠ†…‡‡ˆ‡‡‰Šˆ‰Š‹•œ¢¬²µ·º¹º»º¹³¤˜‘yutux{}ƒƒ†‡……‡…ƒ„„„„„ˆ‹ŠŠ‹Š‹Œ‰‹‹‰‰‰‹‹ˆƒ~ƒ‡†‡‰ŠŒŒŠ‘“‘“—˜œŸ£¤§¬²¹º¹½ÁÁÃÆÅÇÊËÉÊÊÊÉÈÉÇž·°«¥šˆ‚~{xtqrrponnlmnnnmoomnopprsux{yxvrqonkigfgigiklnnonnolllkkklllkmnpoljkqz‚„‡…zslhfccaZTW^dfhikqv{„‰…zvssu}‰‘˜œ +++-/00344455762
+ + + + + + +1s{}}}|zxvvwwvzƒ†‰‹ŠŠˆˆˆ‡ˆˆ‰‹‹‘•™ ¨¬±³¶¸¸¸¸µ°¨ž˜ˆ~usqqqty|€‚†ˆ‰‹ŒŒ‹ŒŒŒŽ’““”“‘‘’“•“’“’Љˆ‡‹ŽŽ’”•““•˜——™˜˜¡¤¦¨©°°·»¼¾ÁÅÅÇÇÈÊËËÊÊÉÈÇÈÇžº´ª¢›’‰~zwutrqpqpnnnnonooopppqqsstwxz|{{zywvutppjhhhjklmmmlmnljkkjjjjkijnrplov{„‚|rjgedb_]USX^cegimsx€‡Šƒxurrx‚Œ“šž )**,-./11214112*
+ + + + + + + ++n€„‡‡…„€|zzzzy~…‰‹ŠŠŒŒ‰Š‹‹ŠŒŽ’—𣫮°²µ··´«¡œ–‹ƒ}vurqqsuw|€ƒ‡‘””••••–˜š›œœž¡Ÿ››žš™™˜•’Ž‘”““•—–™šœŸŸœœŸ§«¯±´¹¼½¿¿¿ÁÃÆÇÇÈÊËÊÊÊÊÈÈÈȼ¸´¬¡™‹‚~yywuspnonnnmnnnoprrrrsttuuxyz{}}}}}}{|{zxunkjkkmnkllmmlljlkjihhhikmqnov…‡…~yrleddb`\VTV\bdfilot|„Š…}wsot|‡•—œ&''(())(''''%$#
+ + + + + + + +"k‹‘Œ†ƒ|~}}„‡Š‰‡ŒŽŒŽ‘•™¤ª¬®¯±´²£šˆ€|yustsqsx}…‹’—™¡£¤¤¢¡žžŸ¡¤¥¦¨©ª©¨§§§¦¥£¡¡Ÿš–•—ššœ››œœŸ ¡¢¥¨¦¥¥§°³µ¸¹¼¿ÀÃÄÄÅÆÇÈÉÊÊÊËÊÉÈÈÇÇÇý¸°¦•Œƒ}zxvvspnmmmmlmnpqqstrrstvwwyz{||}}€€}||}}|{xqmlkmmklkllmlkjjjiighfghiojju€ƒ{qjd^``^_ZRVZadefkkox~„…}xvqqy€‹‘“–™œ!! # ! !!"!
+ + + + + + + +
]˜›š˜•‘Œ‡„„‚~}‚…ˆ‰ˆ‹Ž‘’‘’”–™ ¦ªª¬¬°±¬¢™…|{xusutsyˆ‘œ¦¬±¯°®°´µ·³¯«©ª¬¬®±²¯¯®¬¬ª¨¦¡œŸ¤¦¦¤¡ŸŸ¢¦¦§§©¬®°²³±´·¸»¾¾ÁÄÅÆÇÉÊËÌÌÍÌÌÌËÉÉÈÉÇÆÀ¹´®¦š’ˆ‚{yxvtrpnllkjlnppqrsuvuwvwxz{{{|}~}~~}~}}}||ytqnnljkljljkjjjjhigfggfgjjYU_ijhfg`a^\Z]]ZTRX\`ccdgmpw}€|xtrorz…Ž“•˜™"""" !!##"#"!
+ + + + + + + +Nˆ—Ÿ¡Ÿžš—‘Љ†ƒ†ˆŠ‹ŒŽ‘’’“”–™œ¢§ªª©ª«§›’‰‚€~xwvyz~‡˜¥«£‡mWMJJP_œµÂ½³³³³³´µ³´µ´´±¯«¥¤¦«®¯«¨¦¦§«®°°²²µ¹¼¾¾¼½½¾ÁÂÅÅÇÈÊËÌÍÍÎÎÌÌÌÊÊÉÈÇþµ¯¦ š‘‹|xvutrqonlloqqssrqstvwz{{{|}~~}~~€~~}~}|||xurppnlllkkjjjihihffdeeeicMJKSSTWYXY[[[[ZUQTX]bdeehlqx~}xurnpv€‰–˜˜š› !!"""!"
+ + + + +<€‘Ÿ¦¥¥£ž™”І„ƒ…Š‹ŽŽŒ’““–™š¢¥§¦¦§¨ª©¥™‹‡†‡†ƒ…ˆŠ‹‘š¨±–c<0034453048Gw¯Ä»¹ºººº¸¸¸¹º·µ³±°²¸º»¸³±¯¯²´³´·¸¸¹¼¼¼½¾ÀÁÀÁÂÄÆÆÇÉËÌÌÍÍÌÍÍÌËÊÇÇÅÁ¼³§ ™Š{xursqpponokWZ]aitvuxxxz|~~}~~~€€‚€~~}|||zyxusronnlklljifihfbcbbcf[FIJJMPQRVY[ZZZVQRV[^_cfeimqy}ytrpou~…Š“™™—š› !!"
+
+ + + + + + + + +
)q”Ÿ¥©©©¥¡›–’‹Š†ƒˆ‹‘Ž’••—››ž¡¡¢¡ £¦§¨¥¡œšœ›š›Ÿ£¤§«·§f0(/7;889:9<::5.@{¼Ã»¼¼¼¼»¼½¼º·¶¶¸¼ÃÆÊÆÅþ¸¹ºº»½¼¼¾¿ÀÀÀÂÄÄÄÄÆÅÆÉÌËÌÍÌÌËÉÊÉÉÈÆÄÀ¼¹°©¢”‡ƒzwutqrqpppoy>j{xxz|{|~~~~‚ƒ„…„…„ƒ‚~}}}||||zywvtrollkkjhghgeaababgM=BFIKNOQVYXYXWQOQVZ^`bdgkotyyuopqot…Š’–•˜š
+ + + + + + + + + + + +`Š—¢©«ªª©¤žš–‘Ž‹ˆŠ’“’“‘“–—™šœŸžŸŸžŸ¢§¨ª®²·¶²²²´·¹¼ÄšB'1524:95457;96784.Q©ÉÁÂÀÁÂÁ¾»ºº»¾ÄÇÈÑ—q‚Ž™¶½»»¼¾¾¿ÁÂÃÃÃÄÆÆÆËÐÐÏÎÉÊÉËËÊÈÉÊÇÅÄÁ¾¹´£§c&*),+Mxstrqppont[f|ywxz{|}~€€„†‰‹‹‹‹‰ˆ…„‚}{{||}~~{zzxusrqnllkjhgfccaaac=3<BGIKMOQTWWWSOOSY[_bbdgkptxvpqqosx…ˆŒ’’”•™
+ + + + + + + + +
Gƒ’Ÿ§««¬¬ª¦¢˜“‘ŽŽ”–•–——˜™š›œœš›œ››››Ÿ¦°¶¼ÂÄÂÀÀÂÃÅÆË‹5172692.5=?;963367583A¡ÍÂÃÄÃÂÀ½¼¾ÃÆËÌÒ¯:-343‚ƺº»¾ÁÂÁÁÂÅÆÆÆÅ˨ynad¼ÊÊÉÉÉÇÆÄÁ»º¶°© ››D6trsrqqpnpp'f~yyyz|}~ƒ†‹Œ’ŽŒ‰‡‡„~|{}~~~||{xuuusqnnlkkkheb`cQ248<BFIKMORTUSNMPTX]^_`deioswqppons|„„ˆ‘”
+ + + + + + + + + + +1t›¤©«¬¬¬«¦¢Ÿœš˜”“–—˜››™š››œ›š˜––—˜™ ¬¸¿ÄÆÅÆÈÊËÌÎ|)1:7723Mw³¦c=0:98=;>œÉÂÃÃÃÃÂÄÇËÌÌÌÆV16697P»¿¾¼¾ÀÂÃÂÃÄÇÇÇÇÏo0240KÈËÉÉÈÆÄÃÁ¾¹¶°¨ š“‰//qrqqqqpoyHi€|||}~€‚‡Œ”••”“‹ˆ†„~€€€}~}zxxwvurrrpopmlhdd>.379>DFJLNQSSPLOSUZ^^]_dghnsspnoprv|‚‡ˆ‹ŒŽ‘
+ + + + + + + + + + + + + `‘Ÿ¦¬°¯¯®«¨¥¤¡›˜™˜˜›žžœ››™—•’‘‘‘“˜¢°ºÁÅÆÈËÍÍÏz-58::0N•ÁËÊÈÈÉÊÌǯf59;;:.PÀÆÃÂÁÂÅÉÊÌËÄÇ{+633576œÅ¾¿¿ÂÃÄÃÆÅÇÇÉÊË\49:4eÍÇÇÇÅ¿¼¹¹¶§ ™‹t!-oqqrrsqsck€€‚„†‹‘”˜™˜—•“‘Œ‰……„‚ƒ‚€€€€~|{{zyxuxxvwuturp]/+18;;ADGJMPRQMKQTZ]]^]`dginropootv{|€‚ƒ…„†ŠŒ
+ + + + + + + + + + + + + + +
R€Žœ¤¬²µµ´±««©£Ÿœœœœœž žžœ›•’ŽŽ•¨´»ÀÃÇÊÉÑŠ-4696/sÁÑÉÅÅÅÅÅÆÄÁÃÃ…79<1MœÃÄÃÁÂÅÊÌÎËÂÆ¢//10114.kÅ»½ÀÂÄÄÅÇÇÇÈÉÍÁH6885~ÌÄÅÄÀ½º·²§Ÿ™”‡ƒ`,npqqrsrw1n‚‚ƒ„†ˆ‰Œ“–™™™˜—•’ŽŒ‰‡ˆˆ†††††‚‚‚‚€|~|}|{{{|}|{zyws3%+-3:;?BEHMQRPJJPUY]^_`acdinmlmoswz„…„‡†‰Š‹Œ
+ + + + + + + + +
=yˆ˜¡ª±´¶·¶³®¬«¦¢¡¢ ¡¢¡ Ÿž™“ŽŒŒ‰‹Œ”žª³¸¼¾Àͱ707<71ƒÎÎÇÈÈÈÆÆÅÆÆÅ¾ȉ32a¸ËÃÄÅÆÉËÌÌÉÀ¼¹H&0/0001/?³½¾ÁÂÄÆÆÇÇÇÇÇË«:9874‘ËÁÁ¾º¸³¯ª£œ˜‘Š‚B#kpprspyRtˆ†ˆˆ‰ŒŽ‘•˜™œ›™—•‘ŽŒ‰Š‹ŠŠ‹Š‰‡…„‚~~~~€€}||U#*,7;?ADFKPOKIJOUY\]_``ceiljklov~ƒˆŒŽ‹ŒŽŒŽ
+ + + + + + + + + + + +
'j‚Ž™¡¨¯²´³°¯®®ª¥£¥¥¥¥¤¢¡ žœ—“‹ˆ‡…†‡ŠŒ’©¯²´¶Äs+:<=2ƒÒÇÅÆÅÅÈÈÇÇÇÆÅÅÃÃÉ„ÆÌÅÆÇÇËÍÌÊÆ¾´¼n!--../1241†ÆÀÁÂÄÅÆÅÆÇÇÇÏ”4:889ÆÀ¾½¹´®©¢”Šƒ|zy.eqrrrshv‰‹‘‘’”˜›ŸŸžœš–’ŽŽ‘ŽŒŒ‹ˆ…„‚€€‚„‚€~|r# +!(,7<?BGJLKJGJMRVYZ\]\acehijot}‡”•”“”•••–••
+ + + + + + + + + + + + + + + +
Rx…“ž£¦ª¬¯²²±¯«§¦§§¨§§¦£¡ ž˜‰„„ƒƒ„…†Œ’ž¤«°¶´D-6:5XÅËÇÈÈÈÈÇÆÇÇÇÅÆÆÅÃÁÇÊÇÆÆÈÉÊÌÍËÇÁ·¸”,+,./-/2286VÃÂÂÂÂÂÂÃÅÆÆÅÍz5:89?«Á»¹¸±¬¨Ÿ™ˆ…€|xycZtrrsz;#|Œ‘’’““”—™šž ¡¡žœ™—”“”••••“‹ˆ‡…‚‚‚‚‚‚……ƒƒ‚{~B
#(.8>AGJJJIHJMPUXYZ[\_abeknt~‡Œ’”“”—™˜———˜™
+ + + + + + + + + + + + + + + + +
0u›¢¦ª°°²±¯©§¥¦§§¦¥¥¥£¢¡›“І„ƒƒ„ƒ„†Š“Ÿ¨°¹¡-+052ÌÄÆÈÈÈÉÈÇÆÆÅÄÅÅÅÅÅÇÆÆÇÇÊËËÌËÅÁº¸³E(0./+::279:9¦ÅÀÁÁÁ¿ÁÄÆÅÂÆa3979E¯¼¸²§£œ’‹„ƒ~zxu|ADtsrxZ)…‘‘•––––˜™š››¢£¢ žœš™™š™™š™—•’‘ŽŠˆ‡„…ƒƒ„„†ˆ‡Šˆ‰ƒ|j"(+/9?CHIHFGHLNRUXY[\]^_ajr~…‹Ž‘”•———•”—›œ
+ + + + + + + + + + + + + + + +e€Œ™¢¨«¬¯°³²°®¬ª¨§§¦¥¤¤¥¥¤£¡˜ŽŠˆ……‚‚‚ƒ…Š“ž¨´‘%(),2¦ÄÁÄÆÆÇÇÈÇÆÅÄÃÃÄÅÆÆÈÇÇÉÉËËËÉÆÂ½¸¾s$/0/01˜x-;8;6oÇÀÂÁÂÂÂÅÆÄÃÃS4765F°¶±ª¤¡™•‚€|yxutn"$%prwo"*.“—šœœ››œ›œ ¢£¢¡ŸŸŸž›™—”‘‹‰‡ˆˆ‰Š‹Ž‘’‘‘Š:
&).4<AFFEDFILNPRTYZY]]\]amy‚„ˆ‹“””–”•—›š
+ + + + + + + + + + + +O„ˆ– ¨¬®°±²´²°¯¯®ª«©¨§¨©¨¨§¥¤ž•‹†ƒ€ƒ„Š“˜¥ "$)3¨¿ÀÃÅÇÈÈÈÈÇÇÇÅÄÄÅÆÆÉÈÈÊÊÊÉÈÆÃÁ½Ãš11114,wʪ96589C¶ÄÂÃÃÃÄÆÆÃĺF6763D®¯¨¤ž™’‡€}{xutxN#NWw|F8Y5Œ—›žŸžžžžœžžŸ¡¢£¢¢¢¢¢¡ Ÿ›™—”’‘‘ŽŽŽ’•—š›žš™“`#)-4:@EFDCEIKNPRUVXZ\\[[[`fnuz€…†‰ŽŽ“•–”•“ + + + +
+ + + +
+ + + + + + + + + + + + + + + +,w„“¥«¯°±²³³³²°®¬¬¬¬¬«ªª©¥Ÿ–Œˆ…‚€€‚„Š›~ "* ½»¾ÀÄÅÆÅÅÅÅÅÅÅÅÇÇÉÊÊÊÊÊÉÇÆÅÅÂñ@.4271L¼»¿`066:2‹ËÁÁÁÄÅÅÄÃİ?7640D¨£žš”Š„€}{xutvt-:h-|j}UB—•˜œž ¡¢¢ žžžŸ ¡¢££¤¥¥£¢ žœ››š—””“”“”–˜›ž¡¤¦¦¥¥¡‹, +!'+29@DCCCEIJJMPTUUYZYYYY[\^hmrx{}ƒŠŽŒ‹‹Œ + + + + + + + + +
+ + + + + + + + + + + + + + + + +
\‡— ¨¬®®±³¶·µ³±¯¯®®°±¯®¬«¬©¥ ˜ˆƒ€€ƒ‡$‡º´¸¼¾ÀÁÁÂÃÃÁÁÃÄÇÈÊÊÊÈÇÇÆÅÄÂÂÂÃ]*63352›Â·¿.6685VÃÀÀÁÃÃÃÃÂÆ£7710)D£›—“ŽŠƒ|yywust`Iw3U7\“OI—–™œŸŸ¡¢£¢ ¡¡¢¡¡££¡¡£¡¡ Ÿžœœš˜—˜™š›¡¢¦©«°¯®¬¨¨i
+
$)28?CBBEEFGGJNPSSTWWWXY[[^cfiortxx{€€€ + + + + + + + +
+ + + + + + + + + + + + + + + + + +
9u…‘¥«®¯±²²³´³±¯®®°±°¯®®®ª§£ —އ„~}~„Š6U±®²µ·º¼¿ÀÂÃÂÁÃÄÅÈÉÇÆÅÄÃÃÂÿÈ~'2212,sǺ»¿·B47587 ÇÀÃÄÂÂÀ¾Æ”/2-+#AŸ”މ„~zxvwvtrwEY}Y2–LR˜–šŸ¡¢£¥¥¤¤££¡¢¤£ ¡¡Ÿ ž žœœš› ¡¤§ª°²´¶¸¶¯¬9 + + + + +$(28>BBCDEFFGGIMRRSVUVWX\`_cfhlnoonnsyxy{ + + + + + +
+ + + + + + + + + + + + + + + + + + +fŠ˜ §«®°±²°²´³²¯°°°°¯°¯¯¬©§¢œ–‘Œ†~~~~‰\¨°¶º¾ÁÂÂÃÄÅÆÆÆÅÃÄÅÄÄÃÅÃÂÈ£00113+I½À½½½Ãm08552lÇÀÃÄÃÂÀ¾Å‡+,(%!A—Іƒ~|wwxvuutsv1f|}/yš–M\ž›žŸ ¢¤¦¦¨§¥¤£¢¢£¤£¢££¡¡ žœœœž¢¥¨®±³·¸¹ºº¹¶²¬_
+ + (07>AAEGDGHFGJMPPQSTUVXY[_acedgihkmquw~„ + + + + +
+
+ + + + + + + + + + + + + + + + + +Nz„“ž¥¨¬¬¬¯±²²³µ´±²²±±°²²²¯®«¨¤ž˜“‡ƒ€€~~~!F¥¡§²¶¹½ÁÃÃÄÅÄÄÃÅÂÂÄÃÃÄÅÄÈ¿N,300/7¤Å»»¼¹¾œ13223C¶ÄÃÅÃÁ¿¾Ãx%&" Eƒ|yxvuuvutttr)p‡hO£š™Hd¦ž¡¢£¤¥¦¦¦§¦¤¤¢¡¢¢¡¡¢¢ ŸžžžŸ¡¦ª¬°´¸º½½½¼»ºµ´‰# + +&/8?BBFJFEJHGILOOOPQTVXXYZ\^^acdhjnr}ˆ“œ +
+ + + + + + + + + + + + +
It€Ž›¡¦©ª©¬¯±³µ¶´²±±°°±±°±²²¯§¡œ•Іƒ~}~†Z]¥¡§¬¬¯´»¾¿À¿¾ÂŪÂÅÂÂÂÄÄÃÊl-630.,€Î¾ºº¹¸¹½M-3251ŽÊÂÅÃÀ¾¹½l!% NŽ~|xutuuuuttui"%z‡‰A&‘£ £F ! k§¢£¤¤¥¦¨¨§©¨¦¥¤¢£¢¡¡ ŸŸŸŸž ¥ª®²µº½¿ÁÂÁÀ¿¼¸µ§J + + + + $-8?EGIKJFGIIIHLMLLPRSVXWWY]^`bfltŠ™¢£
+ +
+ + + + + + + + + + + +
Tk|‰”£§ªª¬¯±´·¹¸¶µ³²²²°¯°°°¯®¬§¢Ÿ—‹†ƒ~}}€€„6b¤¨«¯´¸¸»»ÂÂ~?vËÁÁÂÃÀÌ19632/.[…Ÿ²¼¿ÁÂÌ€,5460^È¿»¶ºa !V…}{xvuuuuvvuwc*„’œ}"d®££¨M!!"lª£¤¤¥¦¨ªªª«¬¨¦¦¤¤¢¡¢¡Ÿ Ÿ ¡£¨²µ¹¾ÀÂÂÃÂÂÁ¿»³®k
+ + + + + +,7HGFHJKIGIIIGIIIJLOOTUUVY^`cnt„—›œš™•“
+ +
+ + + + + + + + + + + + + +
agm€Œ—¢¨ª«®±´µµ¹¹¶¶´³³³²³³±±±°®¬§£ž•ŽŠ…‚}~€y$G•¦§®®®³ºÁ®_/83ɾÁÁű?56651--&$,>Qit{…m364459¬ÄÀ¾º´®²S_}{yyzyvwvvxzy|[/›žŸ§V 1¢®ª©«U!$o®¤¥¥¦¦¨ª«ª¬«ª¨¦¦¤¡¡¢¢ ŸŸ ¡¤ª±µ¹½ÀÂÃÅÄÃÃÂÁ¾º°‹*
+ + + +/HKFHJJKKKIKIGHJKNPSSVZ_cglsŠ‘‰‰Š‰ + +
+ + + + + + + + + + + +
$hfg{‹”Ÿ§«¬®±µ¶···¶³²³²²²²³´´´³±°¬¨¤–‹†ƒƒk.g‘§®°²l9'5997’ÈÁÄÄb287670+*+)*-*+-.0076446,tº¸¶°§§Bk{wwyyyyzzz{}†W0˜¤¥¥«ž2#$s¶®«¯] #$k®¨©¨§§¨ª¬¬ª©©¨¦¥¢Ÿ ¡¡ ¡¢¦«±¶»¿ÀÂÄÅÆÄÃÂÁÁ»²£F + + + + + + 3HFIJJKNQQNPRPRTWX^`adjnruz|~|~‚ƒ…
+ + + + + + + + + + + + + +
'kda{•ž¦ª®°²³µ·¸¸·¶µ³³´´³³µ´³³³¯¬ª¦¢ž˜‘‹ˆ…€€}{ƒc'E\a[C+ )12482O»½Ä‡3989:3.%#&'+.232367754351D·ºµ²§ š7$xyxxzzz|~~€‚†Š“` 1¨««¸v'$=¬²±°¬¯`"%$b±ªªªª©©««¬«ª©§¥£¡Ÿ ¢¢¤¦©±µº¿ÂÂÃÄÅÅÃÃÁÀ½¶¯m + + + +6@DCCGMQWWWYYXX\_adceikmoqrtttvx|}€
+ +
+ + + + + + + + + + + + + + +
,nf_o‡’›¦®±³´³³¶¸¹¸·¶µµµµµ´µ³³³²¯«©¦¤Ÿš“Ž‹†‚€{z€`!&(,03.<¿À=79:=6Kš‹`='$(./1466242256*ˆ½±ª¡™’,3yvxy{~ƒ†‡ŠŽ™k"#%3«°²´³J%$u¹²²±®³l$(%\±¬««ªª©ªª©«ª§¥¤ Ÿ ££¢¦«¯²·»¿ÂÃÅÆÅÄÃÃÂÁ¿¹´’(
+ + + + +4;;:>EGMSTWTUTVXZ]`cegfijklqswwy|
+ + +
+ + + + + + + + + +
4rj]cy‹—£ª¯±²³´´·¹¸¸···¶µ´³³´³³´³±¯¬©§¢ž˜•‘‡…‚}||‚s0 #%)+&J¤ÄºÁ_.;;<:7ż»®”vVGC?<>BD91541)I¯¦Ÿ˜‘"E}wz{|€„…ˆŠ‘–œo#%%(/–¯¯°²²¹•-<«²²³±¯µz(*)#P±®®««©©©¨§¨¦¤¢žŸ¡¢¤¦§«®³·½ÀÂÅÆÇÅÄÄÄÄÃÀ¼·¦L
+ + + + + + + + +#5435;=AEJNPQSQSVWZ^_cdddinsuw{†‡
+ + + + + + + + + + + + + + + + +9qj^`mƒ“ ¦«®¯±³µ¸¹º¹¸¸¸¸¸µ´³´³µ¶´´±¯«§¢žš—”Žˆ…~}‚}L"#&f²¿·À-68<<5}ô³³´¹¾»º·´²³¸À–151-,$‡¤—Œy%5/-/...--..(o ¦{!''((±°°³´³»n{º³´³°¯²*(("A¯±±±®«ªª¨¨©§¥ ¢¢¤¤§¬®²·¼ÀÄÅÆÇÆÅÃÄÄÄÂÀ»²u
+ + + + + + + + +%0147:;=BHLOSSQRUW[\_cfhlmtx|€„‡
+ +
+ + + + + + + + + + + +Csk^]f|‘𣍫®°³¶¸¸ºº···¹¹¸¶µµµµ¶·µ³²°®©£Ÿ™”ŽŠ‡‚~ƒuF<е²±¸«>388;4ZÁ¹²²²²³²¶¾ÃÇÉÊÇÆÂN*-*(LŸŠˆp hª¨ˆ*++,*†µ²²´µ´µµµµ¶µ²±°³˜0))$:©´³²±¯¬¬ª©©©¦¢¡£¤¤¥¦©¯´·¼¿ÃÅÆÆÆÆÆÄÄÄÂÀ¼¹@ + + + + + + + + + + + +
,2269:<@DIMPSTRVW[^bfimqvyƒ…ŠŽ’•
+ +
+ + + + + + + + + + + + + + + + +Gvm_]aoˆ•ž£§¬®±´··¸¸¶µµ¶·¶µ³´¶´µµ´´´³±¯ª¦£ž›—“މ†…‚‚…‡‰xP/ ,Gz¡«ª«®´x4;864<¨À³±®¬°¶¿ÅÅÆÅÃÀÄ‚#)($ %‡……b #j¬¬‘1.-.+~º³µ¶¶¶µ¸º¸¸µ²±°´¢6)*)5Ÿ´²³²±¯¬¬«¨¤£¤¤¦¦¦©®µº¾ÀÂÅÇÆÆÆÆÅÄÄÄÁ¾¶©w*
+ + + + + + + + +/48;>BBFLPRUWWY[`dhmty~‡ŒŒ‘
+ + + + + + + + + + + +Rwp`\af{™ ¤ª®³¶··¶·¸µµ¶¶µ´´´´³²³³³²°®¬©§¥¡ž›—”‘Ї‡ˆ‡ŒŠ‚ztt}£££§ª®²§¤¦ š–ž¾¸µ¬©©©«³¾ÅÈÇÆÄ¿¼¹§/$$ X…‡T $d®™5,-,-s¸³µ¶¶··¸¹¹·´²±±³¨<(++.“µ²²°°°¯¯®¬§¥¤¤¥¦§©¬´º¾ÂÅÇÈÉÉÇÅÆÅÅÄþº¶‰c'
+ + + + + + + + + + + + 6:<AEFJORVZ]]abfnruz‚…‡yj]E
+ + + + + + + + + + + +
a{veY^`qˆ˜Ÿ¦ªª¬±µ¶¸¸¸¹¸¸¸··¶¶µ´³´´³²±¯®¬««©©§¤¢œ›—’Šˆ‡†‚ƒ‡‰ˆ‹‘“šž¢¤ª¯µ¸º¼¿Á¿¸¶²¬ª©µ¾ÇÉÇÆÂ¼¶²©®Q/…ƒ…C #]²¯¤8*(,-g¸¶·¸····¸¹¸·´³³´´M,0.)ˆ´¯®®®®°¯¬©¦¥¥¦§§©¬±¹¿ÄÅÇÈÈÇÇÅÅÅÅÄÿº¶£lg$
+ + + + + + + + /:AEILNVZ[]a`chmknkeYI?2(&&
+ + + + + + + + + + +
gywkZ]ae€–¤§ª«°´µ·¹»»º¸¸¸¸¶¶¶¶¶¶µ³²²±®®®®¬«©©©¥ ž™˜•‘ŽŠˆ…ƒ†‹ŒŽ”šš ¥©®®±³¸¹¹¶±ª¨¯»ÃÇÆÅÁº²«¨£¤t*& !p…ƒ< !"#""$'+//447;BDy¸´²„}„Ž’¥·····¶¶¹ºº¸·´³³´¸¥˜˜’‘¨¯¯®®®¬©¦¦¦§©ª©®±¹¿ÄÆÈÇÈÇÅÅÆÆÅÅÂÀº±df
+ + + + + + + + + + +
$*3:<CFHNNOKFD<4)+&"%$&'
+
+ + + + + + + + + + + + + kwwj\^adrŒ™Ÿ£¨«²´´·º»º¸¸¸¸·¶¶µ¶µ´´³³³³³²²¯¬®§¥¢Ÿœ™•‘І„€€‚ƒƒˆŽ“•𠤦¨«²·¸¸·³®«ª±¸¼»ºµ°«§¢–”†{utr}}tw{{~ƒ‡——™¡§±¸¶¶·½º¼½½»¹¹¸¸º¹¸º¼¹¶´´¶µ¶´·¸¸µ¶°¯¯®®¯«§¦¦¨ª¬®°¶¹¿ÄÇÈÈÈÇÆÆÆÈÇÅÃÁ¼±©”cjb
+ + + + + + + + + + + $$!"!# """$%
+ + + + + + + + + + +
"rzyo^_aci|“™ £§¬±³µµ¶¸¹ºº»»º¸·¶µ³²²³³²²²±±°®¯¯°¯®ª¦¢ž™’Žˆ‡…ƒ€€„‰Œ‘”™Ÿ¢§¬²µ´´·µ²±²±²±¯¯©¡—”‘ŽŒŠˆ†‰‹‰‚€ƒ‡‘Ž“”—™Ÿ¤¦¦§ª¯²µ¹ººººº»¼¼»»¼»¹¸¹¸¸º¸¶µ¶¶¶·¶²³²°°¯®®«©¨ª«°µº¾ÃÆÉÉÈÈÇÇÇÆÆÅÄÀ¾º¯¦šmbpa
+ + + + + + + + + + + + + !##rbTE1
+ + + + + + + + + + + + +
%tz{r\]^acm‡–›¡¤©¯³¶··¸¹ºº¼¼¼»¹·µ´²³´³±±±²²²±°±°¯ª¨¦£¢žš•ŽŒˆ†„‚„…†ˆŒ“˜Ÿ¦¬®®¯²µ´´³²³±ª¦Ÿ™’ŒŠˆ‡ˆ‰‡„‚„†„„‡Œ‘‘’˜œ¡¤¥¦«®¯°²µº½¼¼¼¼½½½½½½½»º¹¹·´´µ´´¶µµµ³±´´²±¯«¬«««¬¬®²µ¸¼ÀÅÉËËÊÈÈÇÇÇÇÇÄ»µ®¤›zagpa + + + + + + + + + +
#%œ›š–“‡|dF1
+ + + + + + + + + + + + +
.y}~s^[^adg~‘šŸ£©°²´¸ºº»»º¼¼¼¼¼º¸µ²´µ´²²±±²²²²²¯®®¬ªª©§¤¢—“Ї…†‡‡‰ŒŽ’”™¡¥§«°³¶´µµ··³®¥ —•‘Ž‹ˆ‡‡‡‡……†ˆ†„ˆŠ“•—œž¢¥§¨©¬¯±²³¶¹»¼¼¼¼½½½¼»»»º¹¸¸·µ´²²±±³²²²²²²³±°¯¬«««ª¯¯°´¹½ÂÆÈËËËÊÈÈÇÈÈÇÅÃÁ»´¯¦œ€adjs]
+ + + + + + + + + + + +
"#'•–”’“”˜™™’„j9
+ + + + + + + + + + + +
1}~~ud[]`ddoˆ˜Ÿ£¨®²´·¹º»½¼½½¼¼½»¹º¹¶³²²°¯¯±²°±±¯®¯¯¯ª¨¥¢Ÿ›–’Їˆˆ‰ŠŒŽŽ—¢¦«°²³·¹º¼¹´£–•”‘ŽŒˆˆˆŠŒ‹ˆ‹‘”˜—›Ÿ¤¦¨¨ª¯²´µ´¸º»¼¼»»¼½½»º¹·¸·µµµµµ³³±®®¯®®®®®¬®®®¬«¬®¯²´¶¸½ÀÅÉËÍËÉÊÈÈÉÊÈÇÄÁ¾¸³§œŒi_flu\
+ + + + + + + + + + +
#&(‘”““‘’“’’“—k
+ + + + + + + + + + +3€€zd[^_bdfuŽ˜¥«¯²´¸ºº¼¿¿¾½¾½»ºº»¸´²±°¯®¯°¯°±³²°¯®®®®¬«©§¤ œ™”ŽŠ‰ˆˆ‡ŠŒ‹“˜œ ¤©¯³¸¼½À¾¼¸®«¤¡Ÿ›š——“Ž’’’‘— ¡¢¥¨¬®®¯°³´·¸º¾¿¾½¼¹»ººº¸··¶¶µ³³³³³°°¬««¬¬«ª«««®¯®¯±²³´¸»¾ÃÆÉÊËËÊÊÉÉÉÊÊÉÅ¿»¶²¬¨¡•r\cimq] + + + + + + + + + + + +!$#%%)’’’’“““”’‘g
+ + + + + + + + + + + + + + + +<~€{h\^`cedm‚Œ˜¡¨®²´·¹º¼½¿¾½¼¼º¹º¸¸µ³³²±°°°±²³´²¯¯®®«ªª©¥¢Ÿš–•ŒŠˆ†ˆˆ‹’–› ¥¬±·»½ÀÀ¼·³²¯©¦£ ›˜œ›››Ÿ¤¦¨¯®¯±³¶¶µµ¶¶·¸»¾½»»º¹¹¸¹·¶¶¶´µ´´´³²°®«ª«««¬¬¬¬®®¯¯°³µ¹»¾ÁÃÆÊËËÌËËÉÊÊÉÊÊÈÅÁ¾»·±¯¨¦Ÿacgknq\ + + + + + + + + + + + "#%'&&&*“’’’’’’’‘Ž’S
+ + + + + + + + + + + + + +
>ƒ‚{l_cccedgz‰”Ÿ¨¯²´¶¸º»¼½½¼¼¼¼¼»¹¹·µ¶µ³±±±²²³²²²°¯®®¬«©§¦£¡Ÿš—‘Їˆ†ˆˆ‰Œ•¢§¬³·»¾¿ÂÆÆÄ½»¸±°ªª¨©ª¯°²¶¸¸º»º¹º»¼¹¸··»½¼¼¼»»º·¶µµµ¶³´´µ´²±¯®®®°±°°²²´³³µ¸»ÁÃÆÉËÌÎÌËÌÊËËËËÊÈÆÃÀ»·³°©£¢˜nadgioo` + + + + + + +
$&&$&(&)’’‘‘’ŒG
+ + + + + + + + + + + + + + + +
A‰†~o`bcgihgo‚™¤ª²´µ·¹¹»½¾½½½¾½¼¼»¹···µ³²²±°¯¯¯±°°°±±±²±°¬ª©¦£¢ŸŸ˜•’Ž‹‡†…‡‡ŠŽ“˜¦¯¶¹½ÀÄÄÆÈÈÅÄÁÀ¼º¹¹·¹ºººº»¼¾ÀÃÁÀÀ¾¿¿¾¼ºº»½¾½¼½¼¹¹¶µ´´µµ³´´´´²±°°°±°±±±²³³µ¶¸¸¸¹¼¾ÃÇÉËÌÌÌÌËÊËËËÊÊÈÇÄÁ»¸µ²®¬ª¨¡ž€cbehlrmb" + + + + + + + + +
#&$$'&&(‘Ž‘ŽŽŽŒ‰J
+ + + + + + + + + + + + + + + +
KŒ†ƒ~saachkkkkv…’𤱴µ·¸»¾¾½¼¾¾¾½¼¹····¶µ³²²±±¯®°²²±²³²²³±¯¬ª©¨¦¥¡ž™”Œ‰ˆˆ‰‰Š“œ£«±¸»ÀÇÄÄÅÆÆÆÇÅÃÂÃÄÅÆÅÄÄÅÆÅÆÆÅÄÃÃÃÃÁ¿¾½¾¾¾¼¼¾¼»º¸··¶¶µµ¶´µ¶³°°±²²²µµ¶¶·¸¹º½ÀÂÃÅÇÊËËÌÌËÊÊÊÊËÊÉÉÆÅÿ¼¸´±ª©¨¤™Šjcfikmund# + + + + + + + + + + + + + +
"#%$%'(''ŽŒŒ‹ŠŠ‹J
+ + + + + + + + + + + + +T‹‡†‚va^chjlmklx‡•Ÿ«°°²µ·¹½¾¼½¾¾½½»¸¶¶¶¶¶´³³³³³²±²²²²±°±±²´³³²°¯®¬ª¦¢™•Љ‹Œ‘–ž£¤OHuÆÁÂÃÄÅÅÆÎÑÐÑÊÊÉÈÈËÒÑÏÈÅÉÎÌÍÎËÉÊËÆÀ¿¾¾¿¾¾½»¹¹¸·¶¶·¶·µ³±±³³´¶¹º¹º¼½¿ÃÅÈÊËÍÍÍÌÊËËÊÉÊÊÊÉÉÇÄ¿º¶³¯¬ª¨§¥¢œŽthfiikntmf$ + + + + + + + + +
!!"$$$'&&(ŽŽ‹Š‹Šˆ‡‡‰D
+ + + + + + + + + + + +
^Љ„|b`cfklllgo“§®±´¶¸¹»¾¿¾¼½¼¼»¹¸·µµ¶µ¶´´µ´´µ³´µ³´´´µµ´µµµµ¶´±°¯¯¬ª¤ œ˜—–‘ŽŽ”šŸ¯e ,,žÅ¾ÂÄÄÄÄ~hl˜ÑÊÉÇɽr’ÊÉjhfdbgrŒ«ÇÉÂÁÁÀ¿¿½»¼»º¹¹¹¸·¶µ´´¶·¸¹¼¿ÁÂÄÅÇÉÊÌÍÍÌËËÊÊÊËÊÉÉÈÇÅ¿½¹·´¯ª§¦§§¥Ÿ–‚qiikjkqtlh+ + + + + + + + + "#$%%$#'‹Ž‹ŒŠ‡†ˆ†…ˆE
+ + + + + + +
d•Ž‹‡jbeghllmjjx‹™¢©°µ¶·º»½¾¾»»½¾¼¹¹¸·¸¶µ¶·¸¸·µ···¹¹¹¹¹¸¹¸·¸¸¸···¶µ³³±¯ª§¢žš˜•‘‘‘’”œ–*%,)\Ä¿ÀÄÃÃÇ\)//}ÐÈÅɼE.MÃÌ«312:>;5.6^¡ÍÇÄÅÄÄÄÃÃÁÀ¾¼¼»ºº¹»»¼¾½ÀÄÅÇËÌËËÍÍÌÍËÊÉÉÈÈÉÉÉÊÈÆÂ¿»¸µ³±®«ª©¨¨§¥ ›xmjkkknuvml/ + + + + + + + + + + + + + + + !"#$$$%&&ˆ‰ŠŠ‰‡…‡‡„ƒC
+ + + + + + + + + + + +k•Žˆodfgikklkjp} §¯´¶¸º¼½¾½»¼½½»»º¹¸¸·µµ¸¹¹¹¸¹º»¼½½¼½¼½¼»»»»º»¼»¹·¶µ´°®«¦¡ž™••–•‘œa%$**•Áº¾¾¿Çy076,nÉÈÃÇ]6?±È»H6G«º·³–b-3vÊÉÆÇÈÇÇÇÆÆÆÅÃÁÂÂÁÁÂÄÅÅÈÊÊËÍÍËÊÌËÊËÊÉÈÈÇÇÆÆÇÆÄÀ»·³°ª©©§¨§¨¨¤¡œ“‚qlilklqwxnk1 + + + + + + + + + + + + + +"#$#$%&&‰‰‰Šˆ†„…„ƒ„? +
+ + + + + + +o–‘މ‚qcceggijjiiuˆ˜£²µ¸¼¾À¿À¿¿¿¾¼»»º¸¶´³µ¸ºº¹»½¿¿¾¿ÁÁÁÀ¿¾¿¿¿ÀÀ¿¿½»º¸¸¸¶³²¯ª¦£ž›š˜–‘/!&!L´²¶¸»Ä2622,kÌÄÉn36›ÆÅ[1A¹ËÈÉÍÎ’8+eÉÉÇÉÈÉÉÈÉÉÉÈÇÈÉÈÇÈÉÊÊÊÊÌÌÌÌÌÊÊÊÊÊÉÈÉÇÆÅÃÂÁ¿»·±®ª¥¥¥¦¢¢¤¥¨¨¥Ÿš‘{mllnlnt{|pp5 + + + + + + + + + + + + + + + #$##$%$'ˆˆ‡‡‡„ƒ„ƒ‚B
+ + + + +
u—“‘Š„vbccddfggigk~’Ÿ¨¯´º½¾¿¿ÀÁÂÁÀ¾¿½½º·´´¶¹»¼ºº¼¾¿¿ÀÁÁÀÀ¿¿¿¿ÁÂÁÀ¿¿¼½¼»¼¼º¹¶²¬¨£ ž˜œj.r!†¯¬²³¾-1RR10lÈˇ23ˆÇÈo25¢ËÅÅÄÅÐ6.ƒÏÆÇÉÉÉÉÉÊËÊÉÉÊÈÇÉÊËÊÉÊËËËÊËÉÉÊÊÉÉÇÆÂÀ½»»ºµ±©¦¥¢Ÿ ¢¤¦¨§¦Ÿ•ˆsonnnlou}|ot= + + + + + + + + + + + + + + !!"#&%%‡††…„‚ƒ€}}:
+ + + + + + +
!™”‘‹†{ecc`adffeeiu‰™£«±¸»¼¿ÀÀÂÄÄÃÁ¿¾¼¸··¹¼¼¼½¼ºº½½¾¿¿ÀÁÁÁÁÁÀÁÂÁÀÀÁ¿¿¾½½½½»¸µ³®¨¤ ™—7XžEA§£§¬´)+^¬<4-xÏ–32qÉɆ03‡ÎÅÇÆÄÃÅY1E¼ÉÇÈÉÈÈÈÉÈÈÇÈÈÈÇÇÈÈÉÈÈÊÊÊÉÉÉÇÇÆÆÅ¿»¸·´²³¦£ ŸŸŸœ››ž¡¤§§¦¥›“}npoonmpy€{qx? + + + + + + + + + + + + + + + +
"#$%%%……†ƒ~~|}4
+ + + + + + + +
%…™–‹…{hcc_`ccdeegk|ž¦¬²¶º¾¿ÁÃÃÃÃÁÀ½¹¶¶¸»¼½¿¾»»¼¼»¾¿ÀÂÂÂÂÁ¿ÀÁÂÂÂÁÁÂÁÀ¾½½¼»¶´³¯ª¥št!‚w}¢¤¬Š((RÑ/4,ª72_ÇÆœ56iÍÄÅÃÂÀÈs44™ÌÆÇÈÈÇÇÇÇÇÆÆÇÇÆÆÆÇÇÇÇÆÈÈÆÇÇÄÂÁÀ½¸µµ²²°«¥Ÿœœ™š™™šœŸ£¦¦¥ ˜soqrrpnr…}szE + + + + + + + + + + "$%$$$…„„‚ƒ€~}z|8
+ + +
&†–”†|hcba`abcdfgem|–¢§¯³·¹¼¾Á¿¾¾¾½»¹¶¶¸¹·ºº¹¹ºººº»½¾¾¾ÀÁÁÁÀÀÂÁÀ¿¿¿¿¿½»½»º¶³±¬¥EO”ŠŽE>˜•š ƒ%#I±¶r'-1|;2N¼À¨?7PÀÁÀ¿ÁÃÇb21”ÉÄÅÆÆÅÅÅÆÆÆÅÆÆÅÄÅÅÅÄÄÄÃÃþ¾º·´²®®«©¦§¤œ™–•”’’‘”—šŸ£¦¥¢š“€lpssrppv‚ˆ|v|P + + + + + + + + + + + + + + + + +!#$$$%‚„ƒ€~~|}|{yz=
+ + + + + + +
'ŠŸ›–މ‚kcbb`_``cefedn†›¥¬²µ¹»½¾¼º¸¸¶¶··¶´³³´µ²´µ´µ¶·ºº¹ºº»¾¾½½¼½»¹¸·µµ²°®¬«©§¦¤žž‡azvtYlЇv; Ÿ¦B %%*,<¯»±A2=´¾½½ÀÊŒ67>¬Á¾¾¾¾À¾¾ÀÀÀÀÁÀ¿¿¿À¿¾¾¾½½¼½¼¸µ²¯¬¨¥¡ Ÿœš•‹Šˆˆ‰Š‘•›Ÿ¢¤ œ“…roqsrqpr|†Š}z€Z + + + + + + + + + + + + + + + + + !!"%%&‚„€~|{zz|{xz?
+ + + + + + +
+ŽŸš–’‹ofdb``abbdefghwŒŸ§±¶»»»¼¹¶µ³³µ·¶³±®¯±²³³¶¶¶·¸¸·³°°°¯«©¨¤¢¡žœ››˜–Žˆ‘D*|y~m-“’Ÿˆ# ##%1¥´±E-.¢¾¸»²0-&c³¯³±±³³³´³²±²³³²²²³³³²²°°¯©¤¡Ÿžœœ˜—•’Š…ƒ~€ƒ†‰”™Ÿ¤¦¦£™Švmortrqqu€‹‹}~…d + + + + +
+ + + + + "%%%€}}|z{{zwv<
+ + + + + + + +
2‘𗓆thfdba`abdefhej“¢¨¯³¸¹¹¹¸¸¸¶¶¸¸¸´±®¨§¥¤£¥§©§©««««©«««§¢¢¡Ÿžœš™•’Š‹Šˆ……~pKyte*„ƒ„’c#Š¡ =^sfT80„–‘“—™››œœž £¢¡£¢£¥¤¤£¡ Ÿžš——”‹ˆ…„{{}€‚„‹•šŸ§««¨¢”‚qnprsrqsx„ŽŠ{€†g + + + + + + + #$$&}}~€|{z{xywuC
+ + + + +
+
2’ž›˜’ˆ{jjhda__abfehhfr‰¦°´·¸¹¹º»½¼¼½½¾»¶²ª¤¡žœžœ›ž ¢¡ Ÿ ž—ŒŠ‹†‡„ƒ{|{vxzussrxB$T[]\]^^L!Zja nsrsx?]rx1">gonsz{ƒ††‹‹Ž‘‘“”—™›žœœœš—”‘Ž‹ˆ…~}{|z{€ƒ‡”—ž¢¦®¬¦šŠtmorssrru|‡”‹}ƒ…d
+ + + + + + + !###'}}~}|zxwuusuE
+ + + +
2”Ÿš“‰{mllgcb`abachiho|‘¡¬²¶¸»¼½¿¿¾ÀÁÀÀ¿º´°©¢›š˜™š››››˜–“‹{yyywvtttrrqoolkje<59[kiiffffj]EC@\e@9:_iihhkI+*)Vfg?,1449AIS_hegjmqru{|y{z|€€„‰“—šœš˜–‘ŽŠ†„~{yz|}~„Š˜›Ÿ¤©¬¯®©¡“|mnpttssrv€Ž•‡€‡Œh + + + + + + + + + +!##$'{|}|{yxturquP +
+ + + + + +
7— ž›–Šqqnleb_abcceikio‡œ§®´¶º½¿ÀÀÁÃľºµ±«¦¥¡ž›˜˜™š˜™˜”‘Œ„zvtutsrqrqnmmkhgea^]``^]^[^]^``aaa`^^_`cadeddcfcbb```_^`baaaababdfiknoqtutuuwzzz„Š‘•šœœœš—”‘ŽŠ‡„€~|zz|~‡Ž’™ž¡¦ª«°°¦›smpqtuussy‡””„‚Šo
+ + + + + + + + + #$$&|zy{ywttssorR +
+ + + + + + + + + +
9›¡ ˜’‹ƒvtumhea`bdcehjhiyœ§¯³·½¿ÀÀÃÄÃÂÿ½¹µ¯¬¨¡ž›š›œ›™—“Œ„}wrrsqoponjijffggecdddefecddefgeddcbdcddcdedccabb````^^]^`^^_aaddeilmoopqqruwy{}€ƒ…ˆ–ššœš—–‘ŽŠ‰ˆ†‚~{|}}„ˆ‘”™ž£¦«¯²¯£”~lnpsstuuv–”„…Ž’u + + +!#%%{{z}vvtssrpmI
+ + + + +
+
>¢¡˜“Œ†zuwrnhdabdcehiihmƒ”œ¦®µ»¾ÁÂÄÅÄÂÂÀÀ¿¾¹³¯¬§£¡Ÿž›™˜–’І€{wvrppommlnmmmoononnmmopmlmnmjjjhfghhhhghifefeddedbab```a`bccdfdejnpomoprsw{}~€ƒ‡Š”›žœ˜•“‹ˆ†…ƒ~}|~‚‰‘—œ ¥ª«®¯³²°¬žŒomprtutuvyƒ’™•‡’–x +
+ + + + + + + + + +0!$%&yyzyuvssqpnnP
+ + + + +
BŸ¡¢ž™”‰}u{yrmgcbcdegjjlku…–¢©±·»ÀÂÄÂÂÁÀÀÁÂÁ»·´°¬¨¥££¡šš–’Œˆƒ|zwttrtvwxwwvvwvvuvtrqsrqpqqpponmmmnlmkjjikihghhgfddcccdcdeefghjmoponqsw{~‚ƒ†‹ŽŽ•š¡¡žœ˜”‰‡…‚€|}}„„Š”™ž£§ª¯±°±´²®¥–zkoqstuuuv~Š—œ”‰•˜y + + + + + + + + * "#&&wxwvttrrollkW
+ + + +
J¡¢£ž›•މ€uz{uqleabcdfijkkm{™£¬³¸¼¿¿ÀÀÁÀÀÃý¹·³°«©¥¢ Ÿž›—”“ŒˆŠ‡‡„€€€„‡ˆ……‚‚‚€~|{yzxwxwuuttttrrrssrqnmmonmlllkihhhhiiikikmnpqsusrvz‚ˆ‰‘““•˜š›Ÿ¢¢ ™–‘ŽŠˆ†ƒ~|€‚†‰Ž–œŸ£¨«®±´´´´²«‰qmosutuvvz‚‘›Š“—šz
+ + +
+ + & "#&()ywttrrronlkhb + +
+
N¢¢£Ÿ›•‹„vw|yuqkcbccegikkkr‘§®´¹»¾ÀÀÁÂÂÃÄÄ¿½º¶²®¬§¤¡ œ˜–”“‘“Ž“šŸœœ™˜™—•‘‹‹Š…„‚~}}|zzz{|zwvuvuvvuqsqqppnpqosttwy{ƒƒ„†ˆ–———˜›œžžŸ¢£¢Ÿœ˜“Žˆ…„ƒ€}{~‚†Š•›¡¦©«¯²µ¶··³£‘tnoptvvwxw|ˆ˜œ‹Œ–˜{
+ + + +
+
+ + + + + !' #&*)*ttrsrpmllkjfd
+ +
+
P¦£¥¡›•ˆxx~|ysngabbdghjlmox‹— ª¯µ¹»½ÀÂÃÃÄÅÅÁÀ¼¹¶±®ª¦ œ™–””“‘”••˜™£ª±´´²±®ªª§¥¥¦¤¦¥¤¡ž›œ™˜•””’‘“‘Žˆ†…ƒ‚€€€~~€‚ƒ‚‚„††‡ŠŽ‘™Ÿ¡¡ ¢£¤¤¥¦£¢£¥¤ œ—“Žˆ„~~|}ƒ‡Œ‘—œ ¦©¬¯³¶¹¸¹¸±§œ„lnqswvwyzzƒ›Ÿš‡™››
+
+ + + + + + #'#&*,-.trsqonljjihec
+ +
+ +
N§¦¨¥ž˜“‰}v|~zwqkdaccdgjkkmr}™ ª²·º½ÁÄÃÄÆÇÅÃÃÀ¾»·²®¨¢™–“’“‘’“’—ž¤©±·»»»»¸³±±´µµ¶··¸·´²±°°®¬¬ª§¥¦§¦¥¢ž›š™—•”””•–“’’–—™šš™™š›œ›› ¢¢¥©««ªª«¬«©¨¦¦¡™‘‰„||}~ƒ†‹’˜£§ª±µ¸¹¹··³«ŸŠtmoquwuvyz‰•ž¡–„‘™›
+
+ + +$&!'*-.0sqrpmlkhhgddd + + + + + +
M¨¦¨¥ œ–‘‹€x|~~{umhbcbefijjlpvƒ˜¢¬´º½ÀÃÄÅÇÈÅÄÄÄ¿º¸µ¯¨¢˜”“‘“•žª±·¼¿À¼»¸¶º¼¾ÀÁ¿ÁÃÄÃÃÃÃÀ¾¾¼½¼º¶¶¸·¶¶²±®«ª©§¦§©¨¦¦§¨©¯±³¯°°¯¬¬¯±²³³²±²²´´³²¯¬¨¨¥ –Š„€~~|}~ƒ†‹“™¤©¬®²¶¹ººº¶²£–ynopsvwwwy|‚œ¢¢‘†•œœž
+ + + + + + + + + &&"$+/02pnqmmkffffcac(
+ + + +
+ + + +
M§¦§¤ ž™•…y|~€{tneabdfgjjkou{‡”ž¦®¶»½ÂÆÆÇÇÄÄÇÆÅÃÀ½»µ°¬¦¢›™•’“™¡¨±º¿À½»»¾±±ª§§¥¥ž—˜—–”—¥·ÂÄÀ½¾¿¿¿¿¼»¼¼»º¸··¸º¸·¶¹¹½¸ª˜‡„Œ•£´¿½»»»»½½ºº»»»¹¸·³°®¬¥œ‘Š…€~}„…‰”˜Ÿ¤¨´¶·»¼¼»¹´°§œ†nmpsvywvy|‰– §£‹†–Ÿ ¡~ + + + + + + + + + +)(#!#(/45mmlhkjgeddcb`3 + + + + + +
S¤¨©¦¡™–’Šzy€ƒ€}xslb`bdfgjlnqv~Š“¥®¶»¿ÅÆÇÆÅÆÈÈÈÇÅÁÂÀ¼¸³¯«¨¦£¥ª²¹¼ÁÀ¿½»»¾¼RA=;;<;98<7476=K\¯ÆÄÀÀÀÀ¾¼½¾¾¾¾¾½½½¼»ÀÁ¨~X?320,.39KpžÂÇÀÀÂÃÄÃÂÁ¿½¼º¸¶´¯¥™„~}€†‹”™ ¦ª®³¸º¼¾¾½»¹²¬ tmmpuxywv{„›¤§¡ˆ‹™¡¡£z
+
+ + + + + +((&!#*468jliihhfccc`\[9 + + +
W¨ªªª¥Ÿ›™•‹~w€ƒ€{tpgba`cgijnoru}ˆ“𤝵¼ÁÃÆÆÅÆÈÉÉÈÇÇÈÇÄ¿»¹¶µ´µ¶ºÀ¿¾¼º¹¹¹»±?9:99::8;:6779;;5.@z»Æ¿½¾½¼½¾½¾¿ÀÀ¾½¿Á¢f:+01/430111,*7VŒ½ÆÀÁÁÀÀ¾½¼»¸·µ²ªœˆ€ƒŠ‘—›£§±¶º½¿ÀÀÀ¾º¶¡–}lnptwzxxyƒ‰–¡©©›ƒœ¡¡¢w
+ + + + + + + +%)'"!!%17:jhffgdd`a_[WU=
+ + + + +
[§©¬«§¡š–Žƒw~ƒ‚}wtjdb_`eginnprwŽ– ª³»¾ÂÄÄÃÄÅÈÉÈÇÈÉÉÈÆÅ¾¿¼¼½¾¿½»ºº¹¸¸¸»§63446776546::978874/JœÇ¼¼¼¼¼¼½½¾¿½ºÃµo6)021../210013793/M›Ãº¸¸¶´²°¯¬ª¤›ˆ„„ƒ‚…’•𠦬³µ¸»¾ÀÁÀÀ¾º´®¥•„topsvzxwz}€ˆœ¦«©•’Ÿ£££x
+ + + + + + + + + + + + + ((% "%.6;gdcda``\XXVRQD
+ + + + +
]¨©¬«§¤¡›•’†y~ƒ„ƒ€|wrhb_^`eilnnqtz„™£ª´¸¾ÂÂÂÄÆÉÊÉÊÊËËÊÉÇÅÄÅÄÂÁÀ½¼»º»º··¸º0...0.0012/41,/3345731‰Æº»¼ºº¼½»»¸Á¥L///00010//./0/04421+1y»·²±«¨¤¢žœ–†ƒƒƒ‚‚…‹‘”™Ÿ¥¬¯´¹½¾¿Á¿½¼¹µ¯©›‰wonrsy|yxz…‹–¡ª¬©‘ƒ–¤¦¤§p
+ + %R6%' !$%*17aa`_]^]WVVSRPJ + + + +
cª©¬¬ª§¢Ÿš“ˆ}}„………zunfa^^aejlloru{„–Ÿª·¼ÀÁÂÆÉÊËËÌÎÍÌËÊÊÊÉÉÇÅÃÀ¿¾½½¼¹¶¶µµ•+)))$PŒŒ‘‘„pJ.(10/311’ø¹º»»¼»¹ÂŸ?0111/00),044.++2530...(k¸±®ª§£ š•‹ˆƒ€€‚…‰”šž£©¯´¹½¾¿¿¿¾½º¶±ª‹{opqtw{{{z}„‰œ¦®¨‰‡š¤¦¥¥j
+ + + 3M3$$"%%%,3___\[XXSSUQPLE
+ + +
c¬©®¬¨£Ÿ›–Œ~ƒ‡‡†‚}xslea_``ehjloqv{…‹•Ÿ®·½ÀÄÈÊÊËËÍÎÍÌÌÌËÌËÈÇÈÆÃÀ¿¾¾¹·µ±¯®'&$%q³®±²µ²²´²“K&-,..1;§»µ¹¹¹»ºÂ¥=23/11,);]™ ¡šŒmG--0,,.,"q¯£¢ œ˜”ˆ„‚€‚…ˆŽ”›Ÿ¤ª°µº¿¿ÀÁÁÀ¾¼¹³«¢‘soqswz|{{|€‡Ž• ª¯®¤‰ž¦¨¦¨f + + + + + + + + + + + + +#(1G2$%!$%'-1]\[XWVTRQPMLHB + + + + +
e«®°®ª¥ ˜‚~ƒŠŒ‡†‚|uphebb`bfhjlnsu{„Š‘ž«¹½ÃÈÊÊËËËÊÌÌÌÌËÊÊÈÈÊÈÆÃ¿¼¹µ³²°®¯Ž#$#$ p²¯°±°®²ºµc'-,--'Y¸´µ¶¶¸º¹L010.0++k§½¿ºº½¿¿Ã´:&,)&##$…¢™™•‘Їƒ€‚„†‰Ž•›¡¥©®´¸¾¿ÀÂÃÃÁ¾»·¦›‡vopquy||||„Š‘™¥±®˜Ž¢©¨¥¨^ +
+ + + + + + + + + + +&)*-@/!#"$%+1ZXVRRSROMLHDEB! + +
+
j««°°¯¬§¢žœ“‡€‰Œ‹ˆ†€xsmhfdcbahikmqruy€†Ž™¬·¼ÁÄÆÇÉÊÊÊÊÉÊÊÉÊÊÈÊÉǾ»¹¶µ´±®²#$#%#t´°³µ³°°´´·»]'.+,+-“º³³²³¼q,/-,.(5‹»¶´´¶¸ºººº»Ä«P"&!"?˜““ŒŠˆ†…ƒ„…†‰Ž•¡¦«¯³¸¼ÀÁÁÃÄ¿»·²ª|ooruxz|{|~ƒ‡Œ•Ÿ©°±«“£©¨§§W
+
+ + + + + + + + +&())/A,"$!%%)0VSQONOLJIIFEC? + + + +
+
fª¬±²°®¨£ ž–ˆ€ˆŒŒ‰‚~wpmifcbaaeimoostzƒˆš©´¼ÀÃÅÇÈÊÊÉÊÉÈÈÉÉÈÈÈÅÂÀ½º·¶¶²±²Œ$&#$#p³±³´³´´µ¶´·>'++,&]º±²±´Ÿ4**)+(5𷬮°±²´´³´´´²¶±Lz•Œ‹‰†…„…‡†ˆŠ”œ¡¥«¯³¹½ÁÂÃÄÄľº±ªžrmoswy}}||€…‹’›¤®±±¨†–¤¨¨§¥S
+ + + + + + +((()(+7*$ $%'/SSOMLKLIFEDC?=& +
k«¬²³±®«§£Ÿ™Š€†ŒŽŽˆ„|vrkeddcaafklnqru}ƒŠ¬¶º½ÀÃÆÈÉÉÊÊÉÈÈÈÈÈÇÆÆÃÀ½¸¶µ´±³Ž$%$%$q²®²³´³´´³µµ³¹„'-+,*7§´²±·j#)(')&†¶«®¯¯°±±°°°°±®©ª›1P‘‰‡‡…„ƒ†‰Š”𡦩²·»¿ÂÅÅÅÅÃÀ»µ«ž“ƒspoqvy{~~|~‚‰— ©¯±°¡}„›¥ªª©§N
+ + + + +
))()))+1*"!"'/QOMJJFGECAAA><. + +
p«²²°¯ª¦ œ€€ˆ‘‘Ž‹ˆ‚|wqiggfbbbeimpqsuzˆŽ—£²¹¾ÂÄÆÈÈÉÉÈÇÇÈÈÇÈÉÉļ¹¶µ±³Œ$%$%#t³®°²³´´µ´³³²³®;++*+*‰¶®±@"&&&!X°««¬¬°±°¯¯®¬«§¤Ÿ£g4‡…„†…„…ˆŠ‹‘–š ¦ª¯¶¹¼¿ÂÅÇÅÆÂ¿»µ¬Ÿ•voprux{}~~|…Œ”œ¤±´¯—|Œž§ª«§£E
+
+ + + + +
*)(()((*3*#!#-MIGGHED@?@?=>:/ + + + +
r¨¬²³±°«§£Ÿ˜‡‡Œ‘’Š…€{vojggeda`fhlnorsy~~‡‘¡³¸¼ÀÃÅÅÇÈÈÇÈÉÈÇÇÈÉÇÅ¿½ºµµŒ$&&(%w¶±²µ¶¶·¹¸¶µµ´»Z$))+&m¹¯´—+&$$$+”°ª¬®¯±²±°¯®¬ª¨¤£Ÿš‰''€………„†ˆŠŽ‘”£¦¬±µ»ÀÃÅÆÇÇÅÄ¿»¶¥•„uonqtwy}~~}~ƒŠ– ©¯³²«Ž‘¢«®¬¦=
+ + + + +
#)()(()))*/-" ".GFFDBAA?>>=<;73 + + + + +
sª«²´²¯««©¥ šŒ‚‚‹’“Œˆ†ztnihhfcbbegjmqsuv|€…‘¢«³¸½ÂÄÆÆÆÈÈÈÉÉÈÈÈÆÈÇÆÄ¿¹ºŽ%()*'~¹²²¶··¸»»¶¶µ³¹n())+'^¹²·~$'#%$Gªª«®®¯±°®¬¬ª¨¥¢ ™‘’;'|‡ˆ‡ˆ‰‹Ž‘•œ¡§¬´¹½ÁÆÈÊÊÉÇÇÀ¼º²§™†wnmptx{}~}}‡Œ“›¤²µ²¦…–¨¯¯¬§›;
+ + + + + + +#')(((*((''2- 'GCAA?;<<=;<;876 + + + +
s¬²´´±«ª¦£œ…‚Š“”’‘Œ‡„yrmjihgebcejmprruz|€„‘ «²·½ÀÂÅÆÅÅÆÆÇÇÈÉÉÉÇÅÄ¿¼¿(-+,+‚¸³µ···¸¸·¶´³²·z&)')'U±«±j $#%!_«¥©©©ªªªªª§¦¤¢¡™•‘“G%}Š‹‰ŒŽ”˜œ£§¬³º½ÂÇÈÉÉÈÆÃ¿»·°§›‹|tkortx{~~~~„‹— ©±¶¶±Ÿ~‡›©®®¬¨›4
+ + + + + +
$(('(')('''(0-$DB>=<988987875/ + + + + + + +
oª«²µ´³°©§¦ –‡„Š’••”’‰†‚~wrnkjihfdegimppruxz{Š›¨´¹½ÀÂÄÄÅÅÆÆÈÉÉÈÆÄÄÃÁ¾Å‰-/+-.Џµ¶··¸·µµ¶¶´²¹%)&(%M®¨°W!"% o©¤¨¨©©ª©ª©¦¤¢¡Ÿš–”Ž“I+…Ž‘•—› ¥«¯·½¿ÂÆÉÈÈÇÄÄÁ½¸±¨Œ}ummptw{~€~~‡Ž”›£®³·´¯˜|Œ©®©˜, +
+
+
+ + + + +&('''%&&&'''(/+A?<<;999844543- + + + + +
{©¬±´´³±®«¨©¦™‰„Š’˜˜˜—”‰†‚{upnllkjhgfgkmpqqtvxz„•¡«±·¼ÂÄÄÄÅÅÅÆÇÇÆÅÄÄÄÃÂÈ|-210/–¾¸¹¸¸¹¹¹¸¶¶µ²ºs%&$&#P¬¤ªP #"«§©«©««ª©¨¥£¢¡ž›–“’E3’“••˜œŸ£¦¬±¶¾ÁÃÅÇÈÈÇÅÂÁ¿»´«¢’ulmrtv{}~€€€„Š’™ §°¶¸³©‡{’ ª¯¯¯©'
+ + + + + + + + +(''&&'&&'&$%&%/0<>>=9866543420/ + + +
0ާ®²³´³°¯ª©§„ˆ•˜šš—’Œ‰„ztollllljgghknqqrtuwyzœ¦±¼¾ÁÁÂÃÃÃÄÄÅÆÄÆÆÆÄÃÉo59:7:¦Áººººº»º¹·¶´±·Z!$#% Z¬¤ªQ!xª¦§§¨©©©§¥¤¤¢Ÿ™–“‘“=?˜•—™ž¡¥¨³¹¿ÄÅÇÉÉÉÈÆÅÁ½º´¬¦—‡yrmpuwx|~€€‡Ž—ž¤«²¶·³¢‚‚–¥°²°©Ž"
+ + + + + (''''&&&&&%%%$&+-::::754420//.-) + + + + + + + +
T›¡³µµ´±°®«©¨¢‘……‹’•˜˜—•’Œ…‚~ytommmnmkhiilpqrsttvxy~‰—¢¯¶»¾¿ÁÁÃÃÃÃÃÅÇÆÆÃÁÅ_89>=H²¾»º¹¹¸¸·¸¹µ°¯®?$$"$i«£«Zd©¤¦¦¦¤¥¨§¦¥£Ÿœ›˜–”—Š(Wœ—œŸ£¦«¯²·¿ÆÈÉÊÊÊÈÈÅÃÀ¼³«£—†zrnpuxx|€€„‹•œ¢©¯¶¸¶²—|†š¨¯²²¯ª…
+ + + + + + + + !('&'''&%%%%%%$$$-0776753210.-,++* +
+
{¡ ¬³µ´³²°¯®«©¢•Š…“•˜™š™•‘‹†ƒ}xsppnmnolkkknprsstuusuz‰™¦´º½¾¾ÀÁÂÀÀÅÅÄÃüO:9<?O´¼¼º¸¸··³±±¯ª«“($!!%"§ ©lF§§§§¦¥¦§¦¤£¡ž›™——•˜iu›™Ÿ£¦©´·¼ÆÉÊËËÊÉÈÇÅÀ»µ®¥˜…|tlosyzz~€€€€‚‡‘𠦳¹¹µŒ{Šž«°±²¯¦}
+ + + + + + + + + #'''''&&&%%&&%%##%+/65455300/-,+*)(! + + +
6› ¬³µµ´²±°¯¬ª¤™‹‡Œ’˜œœ˜•‘Œˆƒ~yutronopommlprrsstustvx{…—©³¹»¼¾¿¿¾½ÀÂÂÂÁ±?6324M¶»»¸¶µ´²¯¬ª¨¤®_ ! /˜£ ¥„ %ª§©¦¦§§¤£¢Ÿžš™˜–••@+’ž¢§ª¯³¹ÀÈÌÌÍÌËÊÊÉÆÃ½¶¯¤—‹~tjlstwz{‚ƒ‚ƒƒ†Ž–ž£ª²·º¹´¥„|¢±²²®¨€
+ + + + + + + + + +$('''(('&&&&%&%$%#%,353310///,*'')&$ + + + + + +
\žžª°µµ´´³°¯®«§†Š—žŸœš–‘ˆ…€|ywsqqsrpopqqrrrrstutux}ˆœ¬²µ¸»¼½¾¾ÀÁÂÂÀ¨5.+++O»½º·´²±®ª¨¤Ÿ¢ˆ$M£ž ¡™/ b°©©§§§¤¢¡Ÿžžœ›™•žjO¤ž ¦¬°µ»ÀÆÉÊËËËËËÉÆÃÀ·£—‰}ulkotwxz|„„„„†Š’š ¨°µ¹º¹±š|~”¤²³³®¬{
+
+ + + &)))'&%&&%&&$%$%&%$&+.211/---,*('&&%" + + + + +
#†´¢©¯³´´³³°°¯®«£“ˆˆ”›Ÿ Ÿœš˜”†~|xutuvvutsssttstvvuxxz€¥¬¯°³·¹º¼¾¾¿À¿ÂŸ53,,,Y½»¸´°¬ª¦£•›4z šž£U! !)а¥¥¥¤¡Ÿžžžžœ˜‰.€§¢¥²µ»ÀÅÈÊËÊËÊÊÉÇĽ¸¤˜‰umkortx{~€‚„…„†‰Ž–¤«±¶ºº·¬{†˜¨°³³²®¨
+ + + + + +&***'%&&%%%&%%%$&(&&',/!001--.,))'&&&" + + +
?¦´¤œ©¯´´µ³³²°°°¦–Š…‹”›¡¤£¢¡ž›™•“‰ƒ€|zyyz{zxvvtuuuuxz|~€‚‡¥©¬®±³µ¶·¸¹¼¼Á‘5:497j¾µ²°¬¦¢Ÿš””„9=š˜™¢ˆ ! "#"7˜¬¢¡ Ÿžžœ›˜˜”? A £¦«³µºÁÅÈËÌÌÊËÉÊÆÁ¾¶« œŽ~volosuvz}ƒ„……‡Œ•œ¢©¯²·º¹´§ˆ|‹ž¬²³³³¯¢„6
+ + + + + + +'++)(('&&&&%%%&%&&&&%&(1#/.,,++)%&&%$#
+ + + + + + +
b²´£©²µ´´³³±±±¯©œ…‡—ž¦¥¤¢ œ›™–“‘І…ƒ||~~}zyxxx{}€ƒ‰‰•Ÿ¤¦¨©«®°´´¶·À‚+4361w¿²¬¨¥œ››˜e)›•™ž¤J$""#7‡§Ÿšš™——˜˜›¡Œ@# ¥£©°·ºÁÇËÌÌËËÊËÉÆÂ»¶®¡šƒwninsvwyy|€ƒ„„…ˆ‹‘𠦮±´¸º¸±œ€ ²´³³®š€Y
+
!*-+)(((''&%%&&&'%$%%$$&+/****&%&$$$"! + + + + + +
"‡·¸£ ¨®²´´²´³²²²°©Ÿ‘ˆ…Œ•›¡¤¤¤£ žœ›—•”‘‹‰„ƒ‚ƒ„„ƒ€}|‚†Œ˜£¥›“™šœžŸ ¡¥¨ª®±¯¹q'0/0,n±ª¥¡ž˜b9X˜“”—šžž¢(!#!! *f‘œœ™™˜šœ’h1 !Tª¤ª°·»¿ÆÌÎÍËÊËÊÉÅÀ¼µ®¥—Žƒxoimqtvxy{~„„…ˆŠ‹• ¦«°´¶¸¸¶«“{“£®²µµ²«™€y!
+ + + + + + +
%)++)(((''&&%%&&&&%%%$%%%()(%$##$##" + + + + + + +
5¡¸¹¢ž§¯³´µ³´´²²²²¢”‰ƒŠ–šž¤¦¦¤£¢ Ÿœš˜–•–•“ŽŒ‹Š‹ˆ‡‡†ˆŠ‘™¥µ¶«˜’‘”——› ¢¥§§°]!&'&%,@DAC<90&G‘’•™›œ§t 2Sq}|{qV5 =ž¨ª²¸»ÀÄÉÍÍÌÊÊÊÇÄÁº´¯¤›‘†{qklqstvy{|~ƒ…ˆˆŠ’›¥ª®²´¶¸·´¦†zˆ—¦°µµ´±©–ƒˆI +
+ + + + + + + + +%*++)(((&&%%&%%&&'%$$$%$$%('#$&##$" + + + + + + + +
_¶»º¡©®²³´´´´´³±²¯¥˜‹„†˜ž¤§¨§¦¦££¡ž›ššš››˜–””“‘‘‘–¬¶µµ®©•ˆ„‰”’–™œžž§NNˆ‰ŒŽ•˜œ¦Z#%$! :—©¨®¶¼ÁÅÈËÍÌËÉÈÆÄÀ»µ«¥œ‘†~skkpsrtvy|~„‡‰‹‘™£©®±³µ¸º¸¯žœª°µ¶µ°§•‡o
+ + + + + + + )*,+)((('&'&&%%%%%%$$#%%%&('%%%##! + + + + +
!‡º¿º¡œ©°³³´´´´´´²¯§›‡„‰’›¡¥¨¨¦¦¦¤££¡Ÿ ¡¡£¢¢¡Ÿžœ›ššœ ®µ´¯©¢–…|{|~†‰Š’““–C'b‡†ŠŒŠ‹Œ–˜›¢WBœ®¨¯´º¿ÅÉËËËËÊÈÇÅ¿º´«¤’„}ukmprrsvyz|~ƒ‡‹Œ’˜ ¨±³´·¹»¸—}‚‘ ¬±µ·¶²§”†-
+ + + + + +")++**)))('&'&%%&&&%$%$%%%&&$$$""" + + + + +
+
9¡»Á¹¤œ§®°±³³³´´³³³±«Ÿ‘‰…†Œ”𠤦¦§¦£¤¤¤¤¥§¦©§©ªª¨§¦¥¤£¥¬²¯¦”Žƒzvrsuwy}ƒ……†…‰6'Rˆ„†‰‡‡‰Š’–˜˜™—h" W ¬ª°´ºÂÇËÍËÉÉÈÇÅÃÁº´© šŒztlmorrsuwz|…‰“•œ£ª°´µ¶¸»º¶¦‰{Š—¡«±´·´°¥’H
+ + + + + + + + !*+*+*))(((&&&$%''&%%%%$%%%%$#"! + + + + + + + +
X³½Ã·ªš©°±±²²³´³´´²£•އƒˆ—¡¥¦§¦¤¤¤¥¦¨©«®¯°°±±±±°«ª¬¥™†€{upnmnopquxz{zyzy=-Ce{‡†…‡‡‰‰‹‹Ž”™˜•”‘•C<®©ª³·¾ÃÈËÌÌÉÈÇÆÅÃÀº´®¥›Ž€zvllnqrssuxy~„‰Œ’–›¢©°´µ¶·¸¹·¯šŽ›¤«°³¶´¯¤’–•“n
+ + + + + + + + + +!$*+****))((&''%%%%%&&%%%%%%$$# + + + +
~À¿À¹•¦¬²²²²²³´´´µ²¯¦™‘‰‚ˆ“˜¢¦¦¦¥¦¦¦¦ª«¯±³´¶¶·¸·¶´¯¬¥’ƒ~zxqnnlkkkloprssssspmfca\[X[[[Y]bju|†Š‰†„‡‰‹Š‹’’’’“•’’“’”’zJ))As¢ª³¸¾ÄÈËÌÌÊÈÇÆÅÿ¼µ±¦œ‘‚ztnknprssuuxz~ƒˆŒ“—›¡©®³¶··¸¸º·«“~…‘ž¨²µ·µ°¤š™–Ž8
+ + + + + !"',+******)((('('&&&('&&&%%$#$# + + +
+
.ŸÅÿ¸¯”¤¬²´´³´³³³³µ´²«œ’Š€ƒ‰‹’™ž¤¦§¨§§¦§ª°±³µ·¹º»»¹·¯©›‹€zwuqomjjiikmooopqpppmmklnnmpsx„†‹‘‹ŠŠ‰‰‹’“‘“”“““’‘‘““‘’•‡saOINW`v ¨§«¯³¸¿ÄÈÊËËÉÈÇÅÃþ·³«£˜‹ysokmpqrrtwvx{€†‹“–𠦳¶·¸·¸¹º³¥Š{†“¢©¬³·¸µ¯¤ •˜Y
+
+ + + + + +!(,+***+,+*)()(')(&'&&&%&&$#"# + + + + +
QºÄƽ·²—¢¬±´¶µµµ³²³µ´³¡—‚‚…†Š•œ¡¥¦¨§§¦¦¨ª¯°±²·ºº¼½½·¬©Ÿ’‹wvsonmkijkmnnnopoooonllmllmotz}…‹ŒŽŒ“–•’““”•““•“‘‘‘Ž‘—˜—˜œ¡¢££¦«®´»ÁÆÉËÌÊÈÇÇÆÄ¿½·°¬ ”ˆ~yrjlmnoqprvwxz„‰Ž’–šŸ¦²¶¹¹¸¸¸¹¹°˜~~‹š¦ª®´··´¯¡’£¦—˜v
+ + + + + "(++++**+)))))('(('&%&%%%$&' + + + + +
{ÂÃŽ»¶– «°´µ¶µ´´³³´¶´¯¤š‘†ƒ†…‡Ž•› £¥¦§¦¥¦§¬¯±µ´¾ÄÁ¿À·«±¬©¢—Šzwtrpmnmnoopqpppooppomnopptw{‚†‹‹‹ŽŽŽ‘”––•–––––˜™—“‘‘‘‘““”••˜šœ¡¦©²¶½ÂÇÉÊÊÈÇÆÆÅľ¸±¨¢—Š€zrkonqromoqsy{~‚‰Ž”–›ž¢©±¶¹¹¸¹¹ºº¶§}‚‘Ÿ©¬°µ·¶³®œ¦©”Œ4
+ + + + + + +!(*+,,+*+*+)('''')(&&'%%%%&' + + + +
- ÂÄÅ»½¸“ž©²µ¶´´µ´´µµ´°¨œ“ˆ‚„„‡‹‘—¢¢£¤¤¥£¥¨¬µšPmš»Å¶¶··³¯¥œ’Šƒzvvsrrssusstrrstqqrtuuvuw|„‡‰ŠŽ’•––—˜™˜™˜™™™™–’Ž‘’”••—™›Ÿ¡¥«°µ¸¿ÆÇÉÊÉÈÆÆÅÄľ¹³ªž•Œz{kOB9ATjrqru{~ƒ‰Ž“–™Ÿ¢§´·¹¹¸¹»»º² …}‡•£«®±µ·¶²¯™“¨¬ ––V
+ + + + + + + +")++,,+***+)((('()('(''%&&&& + + + + +
M·ÂÆÆ¹¿¹–¨®²³´µ¶¶µµµµ´²« —Š‚‚„…„‹“šŸ¢£¤¤¤¤¤¦©´Z")0P¬Æµ¸º½»¸·´¥š•‹†}||||zzxxxxvuuvzyyyvvy~ƒ…ˆ‹”—š››œ››››š››—–‘ŽŽ‘””•—šœŸ£¦¬°µº¿ÄÉÊÊÈÈÆÅÅÅÄÁ¼¸±« ˜Œ‚z{_*$Inwx~‚ˆ’–™ž¢¦«²¶¹º¹¹¹º»·š‰š¤ª±¶··³¬–•¨¬£˜˜t
+ + + + + + + + + +!)++*+,***)((())(((&&&''''%% + +
rÅÄÉĹ»˜š¦®²´µµµµ´´´µ´²¬£šƒƒ„ƒ„‡Ž—›Ÿ¢¤£¥¤¢«”*(+)8¨È¶·º»¼¾½¾½»¸¯ª£Ÿ˜“‘ŽŒŠŠ‡…„„„‚€|yxyy|}‚…‰Œ’—›ŸžŸ¡¢¡££žœ›™–•”“’’•–•˜›ž¡¦«®´¹¾ÂÇÉËËÉÇÆÅÄÃÿ»µ®¦›“Š~wzV,f€†’—™ž£¥ª°µ»¼º¹¹ºº»µ¦}†’ž¦«°´¶¸¹³¬——ª®©›˜:
+ +"(++++,*)***+*))())'&&'(''&' + + + + + + +
#ÈÅ˺Á¹›š¦±µµ´´³³³³´·¶¯¦›‘…‚‚„ˆ‘—˜™›ž¡¢£¡ª](*(=µÆ³¬¶¹»½À¿»Â³Ÿ§±¶·´©¨¥¢Ÿœ™—–•’‘ŽŠ…|zx{„ˆ“˜¡¥¦¥¥£¤¤£ žœš˜–••“”–™ššœž¡§«±µ¹¾ÃÆÊËÊÊÈÆÆÄÅÄÁ½¶°§œ‘Œ†{vwY!%k„Š‘•˜œ¢¦¨®´¸¼½»ºº»»º±ž‡‡— ©°´¶··³ª“𫝫Ÿœ–W
+ $)*********+*))''))((''(&%&'
+ + + + + + +
:ÇÈȽ¾Ãº˜¦±³´¶µ³³³³´··³©”ˆ‚}|Œ”––˜šœŸ ¢¡8"&'L»Á³¬´¸½ÀÁÀŸW?GRap†¯¹µ±®«©¥£¢Ÿœ™–“‡}|}~‚‰–›ž¢¥¦§§¥££££ žœ›š™™˜™šžžžž¡¥ª®³¸½ÂÇÊËËÊÈÇÆÅÄÅÄÁ¿½µ¦—‰ƒ{uwa'8ˆ•™œ ¤§¬±·½¿¾»º»¼»·«’€Š˜£«®²µ·¸¶±¦«±¢ž›s
+ + + + + + +$)+++++++))*)*,((((((('&''%' + + + + + + + + + +
S¿ÄÊɼÂú–£¬¯²´µ¶´²²´¶¸¸´¬¡–‹ƒ|zx{ƒ“”—šš›š ‰ %%S½º°ª°¶½ÁÁǽ]088485H²Â¼º¸·´¯ª©¤¡Ÿœ˜“Љˆƒ†Œ”›Ÿ£¥¥¦¦§¤¢¢¢¢ ŸŸžœœŸŸ¡ ¡¥¦§ª¯³¹¾ÀÅÈÉËÊÉÇÆÅÅÄÃÅ¿¸whed_x}ttm,E’“–›Ÿ¡¦«¯µº¿À½º¹»¼º´¢‡„œ¥¬±³¶¸·µ°£Ÿ±¯£ œˆ3
+ + + + + + +%+*)**)*)'(())*'''('&&&&''&' + + + + + + + +
sÆÇÌ˽Âļž’¢¬¯²´µ¶¶´²³µ·¸·°¤™…€~zxx}ˆ“•——– m Z¼²©¦«±¹¼ÃÂ`0234367\ÃÄÂÂÁ¿¾»¸´°ª¥£¡ ›’•œš˜Ÿ¤¦¨«ª©¨©©§¨¦¥£¤¦§¥§¥§©¨ª®±·»¿ÄÉÌÐÓÒÐÐÍÈÅÄÃÃþ¼«:!kxrr8+Pae\?}—•™¡¥ª¯µ¹½¿¾½ºº¼¼¹®—…‡•Ÿ§®±µ···µ¯Ÿ¢®²°¦ŸŸO
+ + + + +'+)))***('()))((())'&&%'''(' + + +
+ + +
(–ÊÊÌȾľ¥“¢¯²´µ¶¶µµ´¶¶¸·²§š†€€{vuu{†‹‘’”œVR³¨¡ ¤«²·¿i35465572zÉÄÆÅÅÄÄÿº¸·³®ª±{9BGQs¬©¯¯°±°°¯®®¯¬®¯±²±³´´¶¸»ÁÆÇÁº´©œˆ…†™·ÊÇÂÄÂÁ·´Ÿ/2tpyS?mtuv|‚f(Vš–›Ÿ¡¥¨³·»À¿¼¹»¼»³¦ƒ‚Œ—¡ª¯²µ·¹·´«š’¤³±§Ÿ –l
+ + + + + + + + +
'+**)+*))***)))**)('(('&'&)( +
+ + +
<²ÈËÌÅÂĽ¤“ ¬±²´¶µ¶µ´´µ¶¸·´«“„€|xttu}ƒ†ŽŒŽ‘A6žŸ™šž¦¬¸|,311566:;–ÅÂÅÆÅÅÅÅÅÅÄÃÀ¾¹¸ªE-,-+Bµµ··¸¸¸¸¸¸··¶¶¶µµ¶º»¼½¼¿Çȼ fYNG@:62118M¹Å¿½º¯ª#Nqqc"Grosw|…j<œœ £¥¨¬¯³·¼ÁÁ¾¼ºº»¹¬ŸŒ‚„›¥«±³¶·¸·²©˜“¥®²±©¡¡“y.
+ + + + +!(*++*))+)*+))))))(()*)('(''&
+ + + + +
+
\ÁÈÌËÃÂÅÁ»§“ª±´µµ¶¶µµ¶¶·¸¸´¬¡˜‹‚€~zvrruz‚‹Œ‹6{š’”™ž©Œ.)*,./37:F¯ÁÁÃÃÃÄÄÅÆÇÈÈÆÄ¿Ãk43.,,2šÁ¼½¾¿ÀÀÀÀÀÀÁ¿¾¿¿¿ÀÁÂÂÅËÄŸsU<6533332//.0/25R¤À¶²¨§t!gln1?uoty|ƒ‰Š’‘Ÿ ¤§¨©°±¶º½ÀÀ½»º»º´¦“‡„ˆ’¡ª¯²´µ¶¸¶±¦”—§¯²²¬¡¤˜„Q
+ + +
!*+***+**)**))*)))'(())('''&' + + +
+ + + +
ƒÅÇÌÊÃÄÇý¬”™¦¯³¶···¶¶···¸º¸°¤™‡ƒ€{xuqpsy}‚‡‰‰/F‰”™Ÿ9'&(,,/56WÀÀÁÃÃÂÂÃÆÇÇÇÆÆÅÏŠ8630163xÇÃÅÆÄÄÆÆÆÆÆÅÇÈÇÇÇÉÈÉÏÉd=54642-,'')),020243A•µ§ œXBpmL.tuv|†‹Ž’˜¡¦¦ª«««¬¯³¸½ÀÀ¿½ºº»¸¯œŠ„†Žš¥¬°²´µ¶·µ°£’œ§®´´°¢¥ Žj
+ + + +!$*.+,+,,,*)****)))**)**(()('(' + + + +
+
, ÅÈÍËÃÇÈľ¬”˜£±µ·¸·······º¹´¨œ“‹ˆ…|ytpnqvy~€‚+!!r‡†Ž˜e !$%).45nÁ½¾ÁÃÃÂÃÇÇÆÆÅÃ΢?5210148S¼ÈÈÉÈÉÉÈÇÈÉÇÈÉÈÊÊÊÌÑf=6651++>TgtthR4-42.00.‡®‘‡:"fmi'axw~…‹•–œ ¤§©ª«ª««¬°µ¼ÀÂÁ¿¼»»¼¶©”ˆ†‰’Ÿ©®°²´´µ¸¶°£“¨±´´¯¥¦¥“y-
+ + + + + + + + +
33-,,,++*+*+*)*)))++**)(()&(( + + +
+ + +
=³ÈÊÍÉÅÉÈĽ®—™¡©°´·¸¸¸·¶··¸¹¹¶ž”Œ‰‡†‚|wsonpruvx+,F=†ƒ‚) %*/3À»¼ÀÁÃÂÃÆÆÆÆĄ̈F4/..-.129ŸÌÈÊÊÊËÊÉÊÊÊÉÉÉÊÉÎÇ‚F523/*<d•°½ÃÁÁÁ½¥d,*,+&q¯¢—Šn"AnoM8~z‰”—œ ¤¦§©ª¨§¨ª«®´¹½ÁÂÁ½»»¼¹°¡††‹—¤¬¯¯±³³¸¹¶°Ÿ’ž©²´´±§¥¤Ÿ‰G
+ + + ++81,,++**+))*++)()***)((('&((
!% + + + + + +
RÀÉÉÍÇÃÉÈľ¯›–Ÿ§®³µ·¹¸··¶·¸¸¸¶¯Ÿ”Ž‹‰ˆ†€{uponmoor;%jlƒ]!%+3”¾»¼½¾ÀÂÃÅÅÄÃ˳K50/-++++,+rÍÉËËËËÊÊËÌÌÌËÉÈи_5824,/i¯ÇÈÄÁÀ¿½»»¿¿ƒ-(&k±¤™‰S]jn0]„„‹‘—š £¥¦§¨©©§¦§¨¬±µ¼¿ÁÁ¿¼»½¼¸«˜‹†ˆœ¨¯°°±´¶¸¸´®›’¡«²³µ²ª¤§¢”g
+!"+38--,++++**+-+()()*++)((''((
!$'),/ + + +
rÇÉÌËÄÆÈǽ°œ–ž¦²µ·¹¸··¶···¸µ¯¤™“ŠŒŠ„~zsqommmqU oB?ˆ01%1¡¾¼½¿¿ÀÂÄÆÄÁĸS/110*))))+*H¼ÍËËËËÊËËÌÌËÊÈѱM4231+K¡ÉÆÄÃÄÃÂÁ¿¼¸¸¸¾…1d®¤œŽ‚}4.nmg x‹—› ¤¦¦¦¦§¦¦¤¥§©®±¸¼ÀÂÁ½»¼¾»´¤‘‰‡‹”¢°°±³¶¸¹·³«—”£¬±³µ´¬¥ª¦žƒ<
+ + + + + + + #*.69-,,,+*)*)*+**)))*,+())))("&,-/005&
+
*—ÈËÎÌÅÈÉÇÁº°œ”›¦±´·¹¹¸···¸¸¹¸±©œ“މ~zvsmnnmj%XpolD[3¢¸¶¾ÀÀÀÀÂÃÃÅÀ_3522-9I'*+./ŽÐÈÉÉËËÊÊÌËÊÇͳO3311)`¾ÌÄÄÅÄÄÂÀ¾¼¸µ´±¯¯”¨¥Ÿ–‹~m%Dwu[0‰Œ‘™ ¢¤¥¦¦¦¥£¤¤¦¨°³¸¾ÁÃÀ½»½¾¹¬™Š‰‰˜¥±°²´¶·¹·°§–—¦±´µ´¦©¨£•^
+ +
!! #%+,-:7,-,**)))*,*)*))*))***))( $)+-0101* + + + + +
?²ÆÍÐÍÅÊÉÅ¿¹¯ž”—£«¯²¶¸¹¹·¸¸¹¹¸¸´« •‘“”‘Žˆ‚€|wuqomp::yO8=xc5¢¬®´¹»»»½¿ÀÊ|4:6479—σ(,+-.XÆÇÈÉÊÉÉÉËÊÈÈÆ]3400(eÄÆÁÂÂÂÁ¿¿¼¹·´²°¯®¬¬¥Ÿš’‡‚eS…ƒT;’˜Ÿ¡¢¤¤¤£¤¢¢¢£¥©¬±µ»ÁÃþ»¼½¼²¤‘‹‹“ž§°°±³µ¶¸¸µ¯¥•™§¯²µ¶´¯¨©¬¥•t, + +! ").-,181,+*)**+++)*+*(()(),)(*( ""$'*+..1200. + +
^¿ÅÍÐËÆÌÉþ¸± •–¡ª°´µ·¹¹¸¸¸¹ºº¸µ¬£™‘’—˜”‰ƒ€}zwrnpO%kv,CŒi2𤧲´µ¸¸¶Àš866675|ÇÄ7../17¢ËÇÉÈÈÈÇÉÈÅË‹4402/I»ÂÀÁ¿»¹¸¶¶´³°¯®««©§¦¢›–Œ‰eX“Y:˜˜›Ÿ ¢£¤£¡¡ ŸŸ¡¤¥©®²·½ÁÃÁ¼»¾¾¹¬šŒ‹‹Œ˜£«°°²³¶¸ºº´¯¡’›¦®³µµ´±ª¨°ªš…P
+ + + + !!&..,+49/++**++*+)***)()((+*('(!%()))+.///13331
ÆÈÎÐÊÈËÇþº´¤—•Ÿª°µ··¸¸¸¸¸¹º¼»¸¯§•”š™“ŽŠ…ƒ€}xstaWyalˆm.”ž¡¦¬®±²³¹´I*0-/.Wº½¼¿Y-2013pÍÇÇÇÇÆÅÆÆÈÂ[21041ü¾½»¶µ´²²°®¬«¨§§¤¡žš”“‹Œj R™“g0”Ÿ ¢¢¢£¤¢ ŸŸž ¢¥«°µº¾ÁÁ¾¼¼¾¼²£”‹‹Š›¦®²°²³¶¸º¸µœ‘›§¯´µµ·³©±ŸŠt& +
+ +##$#!#+,./-.65--+*+*)****)**)*)))))) '''&(0./20021/445558
*žÈÊÎÎÉÉËÆÂ½»¶¥—“𥮴¸¸¸¸¸··¹¼¼»¸³ª ™‘—š˜”Œ†‚zvn$Auu=6z|p/“¡£¦ª¬ª·u%)*)+2™º¶·¾€*1//1I¹ÈÅÆÅÃÃÂÂÆ¬>3./3Aº¸··¶³³²°®¬«©§¦¦¥¢ž›—“‘‹Œl H˜”w!!ƒ¡ ¢£¤¢¢ Ÿ Ÿ¢¦®´¹½ÁÃÀ¼»½½¸¬šŒŠ•ž¨¯³²²µ·¹º·³ª–©°³µ¶·¶¯©²¯£F
+
##$##%,,,/0.164.-,+********++*)))**(((+.0/4956:758;89=@9:;7
B±ÇÎÏÍÉÊÊÆÂ½»¹§—“˜£«±¶¸¸¹¸·¸¹»½¼ºµ®£›’‘˜š˜”‹†„‚€{x(/rrkW}{y%/ššœ ££¤¨œ3$&%'%c·±³µ¹¢1,.,14ƾÁÁÁÁÀ¿Å21,+0T·´µ´²±°°±¯®¬°®®®¬¨¤žš“‘‡†‹‘z$<”“%d¥¢¤¤¤£¡ ŸžŸ” ®°·»½Á¾¼¾½»³¤”ŽŒ‹Ž–£¬±±±²µ¹º»¹²¦‘’ ª²µ¶¶··µ«®¯¥‘…f
+ + + +$##$$&-/.11-/172-*-+*****,*)()*))***(),1-.11011345578<<==<5
V½ÇÎÎËÈÊÊÆÁ¾¼¸©š“™¢©®²¶¸º»º¸¸¹¼½¼¶°©Ÿ•Ž”›š—•’‹†…ƒ~}/'suyM#r}|}**„”•“”˜™›¤b 1›¯¬®®³S&*(+*_¿¹ºº¼¾¾ºÁ}**)(*]µ¯°®®¬¬«ª¨©s`b_[USKC<81`‘’‚)0Œ•Œ,:¢§¥¤¢ œœœœ JV¸¶¼¿ÂÀ¼½¾½º¬™Œ‹Œ“ž¨°±±±³¶¹ºº¶¯ •£´µµ¶¸¹¸°©™…z,
+ +%$$%%)-0/00-,-26.+,,**+**)**(**))))),+-../00//3355565799:95 +
vÃÇÎÎËÉÌÉÄÀ¾½º¬”–¡©®²³·º¼¼ººº»¼¼¸°§¢›“‘–››™–’†„„…3"mzvu)E€…‰0*…Ž‘’‹,X¢¢¦§¨§®s$#'&5¡¸±±³¶¸¸¼r%%%#$T®¨¨§¦¦¤¤¢¢¢¡C!Q’‹/(„““@ f«¡¡¡ž››››˜¤w'$R«¿ºÁÀ½»¾¾¼³¤“ŽŽ˜£«±²²±³¶¸»¹³œ‘—£´µ¶¸¹¹¸°«¯¡ˆ€Q + + +&$$%&*///0/-,,-45,++++++++**)))()*)*3/000.10038766777596987!
&˜ÃÉÏÏËËËÇÃÁ¾¼º¯ –•ž¦¬°´¶¸»¼º¹º¼¼¼º´«¢–’’˜š›™—•ކ‡‡2!o~{}\f‚ŠŽ3+‡ŽŒ‹ŒŒ‘\$|”—›žŸžž’* #j±§ªª«ª«±m""C¥ žžœšš˜™œ›> T‹Œ’4!}‘•_ '~ª Ÿ››š™— ”1"'$D¾¿¾º¼À¾¹¬˜ŽŒ“§¯±±±²´·¸¹·±¨—˜¥³·¶¸¸¸¸°¨¯±©’€r
+ +"&$$&(,//0/.--,-.72++++)*(*))))))))**6324301128;667:88:=8:9:( +
=¯ÄËÐÐÌËÊÆÃÀ¾½»²£˜”œ£ª¯´¶¸¹º¹¸¸»½¼¼º°¤›—’“•˜šœš˜•‹‹8"sƒ€~32„‡Œ‘1*„‹ˆ†…ˆ…25‚”˜œŸ¡šžQ2™¢¢¢ ¡¡¤o/Ž“’‘Ž‘–˜“7_ŒŒ’: y’•„'#.~§žšš™– œ='&(&~û¸¹¿Á½¶¥“Ž˜¢«±²±±³µ·¹¹µ¯¥–‘𥮳¶·¶¹¹¸´«°³¬œ€„5
%''&&),...-----,,091,,.,*)**())))()++<8798549<9:6::<<=AB?@A@7 +
f¹ÄÍÏÎÌÍËÄÂÀ¾¾¼´¥™•›¡§®²¶¸¹¹¹¹¸»¾¾¾»³©—“‘‘’•˜š›š—•’’:!x‹‡ƒŠa^‘“–‘0(€Ž‰‡ˆ‡Žd(6@JOT[`cIh¡—–—™˜›z]‡‚„…‡ˆ‘”–’1_ŽŒ7 v–•žK! )qœ¡›ž¡‹>&%&%9®¼µ¶»Á¾¸š”𦮲²²²´¶¹º¹¶£’‘œ§®²µ¶·¸ºº¶¯³°£‹ˆ[
((*))+,///.-,-.-,,27.,,-,++*)))))()**<<=><?=BD@@>AB@CEEFEHHGO +
4™ºÄÎÏÍÌÍÊÆÂÀ¾¿¾·¦›“𢍮²³¶¹¹¸¸¸º¼¾¾¼¸®£™–““–˜˜™—–’4&}Š‹‡>‡™——˜‰&#yŒ‡†‡Š‹6-‹‘І}.,{‚ƒ‰”••H.--/-$fŒ‰†3"{“—Ÿ+ FrxZ&#"$$²²¸ÀÁ½°ŽŽ‘— ª°³³²²´¸»½»·¬ Ž’žª°³´´·¸º¹·®®²±©•ˆy
()*+,......--,-..-.22,,,+-+****)*)()(;<<?BDABEGDEIJFFGIKIILMT4
e±ºÊÏÏÍÌÎÊÅÂÀÀÀ¿¹ªž‘—¢¨®°³¶·¸¸¸¹º»¼¿½¹²¨œ—“‘‘‘’’”•––˜’.)‘“‹–˜š›™!q‡„„„‰lU†„ƒ~}€FC}}‚„‹ŽŽŽŽ‘’“•sm‡„0$~–™œ§t"$#!"$%_Àµ¯´¼Á¼µ§“ŒŒŽ“›¥²´³²³¶¹¼¾»µª—Œ” «°³´´·ºº¹¹±ª¯²¯†ŠG
')))(*.--.,-/,-013.063,+*,-*)*+*****)==>DFEEFKLLJMNNOKNLMILR]C
.œ³½ÎÐÎÌÍÌÈÄÂÀÀÀÀ»®¡’“Ÿ§°µ¸ºº¹¸¹º»¼¾½ºµ¢™•“‘‘’“““”•••tH'-†“˜›šš™™~k‚‚ƒ‡?#xƒ€{{zkJ‚‚‡ŠŠ‰Š’•™˜”–tnЇ+&ƒ—› Ÿ¡b ""!!"#T¸¸®±»Á¿¸«›Œ—Ÿ¨±µ´³³µ¸»½¾¸°¤•Ž—¡«¯±´´¶¹¹ºº³ªª°±¤‹‹k +
*()((&).0/.-././12:82270,++++++++*))*+BBFIJJKKNOQLNOOPNOMLOQPSD
\¹°ÁÍÏÌËÎÌÆÃÁÁÁÁÁ½±¤•’𥬲¶¹»»¹ºº»¼¼¼»ºµ°§ž˜”‘“––—•’“••œˆ}—“‘”™œœ›˜–‘{"`„€€„r!'M‚}zx|OG‡ˆŠ‹Œ“•–•”\!u‰†t#+…–œŸšŸV!""$#f½¹¯®µ¿Â¼²¤”ŽŽ”œ¥°³¶´³³µ¹¼¾¾¸¡”™£ª¯±±³µ¸º»¼·¬ª¯²¬”†ƒ.
#*((('(*.0//..////0<N6-47-*+**+**++)(*,ILJIKLJJLOSRQOOMMNOLONLIH
#–¿´ÅÌÍÊÌÏËÇÂÁÂÁÂÃÁ´¦˜“˜¡©°µ¹ºº»¼»»¼½¾½»·²«¢š••–˜———”‘’•–— ›˜–˜šœŸœ˜•y"W‡€~„D uƒuncYNJCA=81-*h{z|{xuy76rŽŽ’“–˜š˜š™s#$|‹‰r,ˆ—™œž Ÿ] !"$";‹Ã¾²¯³½ÁÁ¸ªœ“˜¡³´¶´³´·º¾À½µª‘š¤ª°°±´µ¸º¼»¹®ª®²®‡‹O
!'''((**-../.-//.,/1KK-.78**+)**)*+*))*LOPPQRQONOPSSRSPLNONJKNMM1 +
YÀ¸¶ÆËÉÊÎÏËÆÂÁÁÁÃÅÆº«š“•œ¦¬²¶¸º»¼¼»»»½¾½º´¬¦Ÿ˜•–—™š™—”‘’•—˜™œŸŸ™–—˜›™–’|%Jƒ~€v#D‡ƒ‚…ˆŒŠ‰ˆˆ‡†ƒ~|c7{ywxvtvv/ R„˜ž ¢£žŠW,ˆŒŒk*‡œœŸ œœo8!*I€²Ä¼·¯²»ÁÁ¼²¢’ŽŽ‘•›§¯´´µ´´³·¼À¿½²¥–’¤ª¯±´µ¶¸º»»¹±«±³©‡q
&''(''(+.../..-..-/15B7,/63,+*)()**,*(*PSSPSXTPSMPPRRSPNNLKMOQROA
šÆ´ºÊÍÉËÏÎËÆÂÀÂÃÄÆÇ½¬ž”•𢩝³¸º»»¼»»»»½¼»·°¨¢œ—––˜›››–“’“””’“˜žœ—–••”’'A}ƒWn‡ƒ…†„†‰ˆ‹‹‡…‚~{€AW}xxywy€x1%=[nxvfG*WŒ‰e)‡Ÿ £¢ œ™–š“v`Ycw—³¼¼»¹²²·À¿¶©—Ž‘”˜¡¬³³²´µ´µ¹¿¿¿»¯Ÿ’“ž¦«¯³´·¹º¼»»»µ©«¯²¯—‡‡='(''&((*.../00-,-./1034-,05/**+*,,++*)*WWWY]^[YXTXSZ]ZURRSQPRWTMK
VÅó½ÌÍÇËÑÍÇÂÁÁÃÄÅÇÉÀ° “”™ §¬±·º»»»»»¼¼¼¼»¸³¬£˜––™œžžœ—““’‘’’˜™™—”‘Žˆ†„?#7}|/?‡ƒ‡‰‰ˆ‡‰Ž‹Š‡ƒ}||t ,y}{}ƒƒ‰‚=E’‰ˆŒh&„žŸ Ÿœš˜——Ÿ¢¤¨ª±µ»»ºµ±·¾ÂÁ¹ž”‘’–œ¦¯´´´´´µ¶»ÀÀ¾¸©›•¡¦¬±³µ·º½½¼¼¼¹®®°±¯ŸŠˆd&))''()(,/.-010/.-/110/1/,-26,)(,..-,+**UUUVXZ[ZXYYTY`^ZWY[YR\[YVZ$ +
—Ì¿³¾ÍÊÅÎÑÌÄÂÁÃÄÇÈÊËĵ¤•“–¥¬³¹º»¼¼»»¼½¾¿Àºµ¯¥ ›—–™œžŸ¡—“‘“•——•ƒ€~{vpmfj€‡B-$g‡‰Š‰ŠŠŠŒŒ‡„~{zULƒ€†‹ŠŒ““T:’ŽŠ‹i zŸš˜˜–—šœž¡¥¨¬¶º½¼¹µ´¼Â¼±¢–“‘”š¡«³µµµ´³´¹¾¿¿¼³¤•‹˜¢©®±³¶¹»»»¼¾½º±®°±°ª’‡|)*+*)**))....-//...020/-.//,,43-++,,,,+**NOQQORRRSPQNMSPNQPQSSWVWVU5 +
N¿È¾²ÁËÇÆÐÐËÄÂÂÄÆÊÊÌÏÆ¹§—’”›¡¨°·¹¼½¼»»»¼¿ÁÀ¿¹²¨¢Ÿ›˜——›ž¡ œ—”“’’””’‘““Žˆƒ~~ƒ‹”™ž–‹€qjŠŒŽŽŽŽŠ‰†ƒ€~~4#r‡Š’“””—œy0?ŒŽ‰‹‰‹`({ š˜˜—–˜™œ¡¥¨®µ¼ÁÀ¼¸µ¹ÀÁ½³©›“’’•œ¦°¶¶´´´³µ»ÀÁ¿º¡’Œ›£¬±²µµ·»»»¼¾½º²¯°±±¯Ÿ‹†I ++***))*./--...//.2510.../-+-64.,(*,,++*PRWVPOQTXTTRQQLLQOOOSVSTTSK
’ÊÆ»´ÅÉÅÇÒÐÉÃÃÅÇËÌÌÎÐ̾«™“–™Ÿ¦¯³·º»»¼»º¼¾ÀÀÀ¼¸®¦¡š——˜œžŸœ™–”•”•”‘Љˆˆ‡††‰Ž”šœž£¥¥£¢ž—–•––”“”‘Œ‰‡„€‚x][\[Z]|‘“—˜šžž Ÿ¥š_&U’ˆ‰ˆ‡‰hH[vŸ žš–—–—˜šŸ£¦¬µ»ÂÃÀ¼¸¸ÀÂÀ¶¬ “‘‘’•™¡´·¶´´²´·½ÁÁ¼³¨šŽŒ“ž§®²´µ·¹º½¾½½¼»³¯°²³²¨‘‡i,0-,,+++-//.-.///0/254//...-+-/71.+*++++*TXZXVQRYb^[[ZYXV\YVUZ][XW[_
TÁÆÃ··ÈÈÃÉÑÏÇÄÆËÏÐÎÎÎÑÐÃ°š””—›¢«±µ¸»½½¼¼¼¾¿À¾¼º³ª¥ ˜—˜œž›š˜•”’“””’‹Š‰‰‹Ž’–˜›œŸ¡¤¦¦¦¥ œœ›™—˜–—–’ŒŒ‹‹‹ŠŠ“•“–šš—™œž¡£¦¦¨§¦§©’`2D{•Œ‡ˆˆ‰‹Œ‘Ÿ¤¤¦£ œ—”•–™ ¢§«²ºÁÅÅÀ»º¿ÂÁ»°¢“‘““—Ÿ©±¶¸¶´³³µ»À¿¸®£”– ©°´¶·¹º¼¾¾½½¾¾µ°±³µ³¯›…D62/-,--.00/..//...04511.-.--,+151-+*++*+TUTTTQPPVZUUVVVWWUWY\ZZZ[[[)
"“ÍÈÁ´»ËÆÂËÑÌÇÇÉÎÒÒÏÍÏÓÒÆµ —”“˜ž¨®´¹»½»¼»¼¿¿À¾½»¸±«£™˜–™Ÿ ›˜——“‘’•”‘‘Ž‘““’“•™œœžš™›Ÿ£¦ª©¨¤¥¦¢Ÿžš”‘’’””•š›Ÿ££¥¨ª«®®¬««¯Ÿ‚hT>638Kd…™“‹‹‰ŠŒ‘”šŸ ›–••–˜› ¢¥¬µ»¿Åľ»¾Â½±£—‘’‘“”›¥¯µ¸·µ³³´·¼ÀÀ¾¶¬Ÿ‘Š™¡ª±´·¸¹»¼½¾¾¾¾¾µ°±³µµ³¤Š‰Y2/+-.++.00.//00/-./1420/...,+,/43.+****+SQRSRQPWWWRQQTVUSRTUWVSUTTW9 +
PÁÈÈ¿²¿ËÄÁËÒÌÉËÏÒ×ÒÏÍÎÑÔʸ¡—”“”˜¥²·»¼¼»»¼¿¿ÀÀ¿¾º´®§¢›˜—šœžŸž›™™˜–’’–˜œœŸ ŸŸž››ž £§ª°³¯¯©¦¢—““••““””—šœŸ£¥©¯°±²³´µµµ¶¶µ´³®®«¨¢œœ ¡•ŽŒ‹Ž‘‘“•˜˜™šš˜’’“”—𣍬³»ÁÄÄ¿¼½Âý·§™“’‘’‘”™¡¬´¸·¶¶µ´¶º¿ÂÀ¼²¦–Ž‹‘œ¤°µ·¹º¼½¾¾¾¾½¼·±±³µ·¶«‰q4.,--*).///0/.//../07600.-..,+-/44,)**++[Y]ZT[[^eaZ]YX[YW\XXXW[XYVSG”ÎÈȺ²ÃÈÂÂÌÒËËÏÓØØÒËÌÍÑÔË»¤—”•”•¡«±¶¹¼½½»½¾¿ÀÁÀ¾»¹³¬§¡ž›š™šœŸžžœš™™”Ž“—œ £¥§¦¥¦£¢¢££§§¤¤¦¨«¬¬§¢™‘‘““•—–™œž¢¦ª®²¶¸¸¸¹º¹º¼»¼¿¼¸·µ´°©§¤£ œ˜“Œ‹Œ‹Š‹‹Ž‘“””’ŽŽ’•˜œ¢¨®³ºÀÆÇÄÁ¾¿Âľµ«”’”’‘“–œ¥°·¸¶µµ´µ¹¾ÁÁ¾¸¯ Š‹•Ÿ§®´·¸¹º¼¾½½¼»»»¶°±³µ·¶²™Š‚E20,)++.0/./../../.0<C4/00.-,,,,05.++,++UVUSTWUV[[X\VXZ[]_`acac_b^YT
LÃÌËǶµÅÈÃÃËÔÐÏÕÙÚÖÏÊÊÍÒÕ;©˜””“–ž¨®´¹»¼¼¼¼½ÀÁÁÁÀ½º¶±«¤ ŸœœŸžš˜˜•”‘‘•£¥©ª««ª¨ª©ª¬¬«ªª©§¥¢ š—“‘ŽŽ”–˜š¡§¬³¸¼ÂÃÁÁÁÃÂÁÃÂÂÀ½»º¹¸¶²®«§¢—Œ‰ˆ‡†„†ˆŠ‹ŒŒ‹‰’•›¡¦¬²¹¿ÅÉÆÁ¿¿Â¿· •“””“’“˜ ©³¸¸¶´³³¶¼ÁÂÀ¼´©˜‹‡Œ˜¢¨°µ¸¸¹¹»½¼¼»º»¼¶°³´¶·¸³¤‹c10-**,.0/////....-.2<512/./-,+*-23,*,+*MKPOPSRQNRONMQRTVTUUWWXZ[WTS%ÐËÌð¶ÆÆÃÄÏÔÔÕÖØÔÏËÇÈÍרÐÁ«™”•““›£´¸»¼½½½½ÀÀÁÁÁÀ½¹µ²ª¤¡ŸŸžŸ¡ ŸŸžœ˜—–•’‘’“—› ¤§ª«ª¨§¨ª©©ª§¥¤ ›–’ŒŽ“˜œ¢¤«°µ»¿ÂÅÈÈÇÆÅÆÄÃÂÂÁ¿¾¾½½»»¹¶³¯ª£•‰‡„„„……†ˆ†…†‰‰Š‹Ž”›¢¥¬²·¼ÂÅÆÃÀÀÄÄÀº°¢—‘“•’“”𧝶·¶´³²µºÀÃÂÀº±¤’‡…𥫱µ··¹º¼¼»º¹¹»¼´°³µ·¹º·¬–Žy80.+**///..0.---,,-.02351-/.,++-.53--+)MOPSOQRSQSSXQSSRSSTRPRSSTSSO>C¾ÍÍÌÀ¬¸ÉÆÃÄÌÔÖ×ÕÔÐËÈÇÉÌÖÚÒ“‘‘”ž¨°µº¼½¾¾¾¾¿ÀÁÁ¿¼¸µ¯¨¤¡ Ÿ ¡¡¡¡ ›š™™—••—˜™™šœŸ£§¨¥££¢¥¥¥¥¢Ÿ›—”ŽŒŽ•—¢¦¬°¸½¿ÂÄÅÆÅÇÇÆÅÄÂÂÂÀ¾½¼º»¼½¾»·´¯¨£œ’ˆ…„‚‚‚„„„††…‡ˆˆ‹‘—¢§«°¶¼ÁÅÇÄÀÁÄÆÂ¹±¥™“’•“‘’˜£®´·¶µ³²´¸¾ÂÅÿ¸Ÿ‘‡…‘§¯²´¶¸º¼½½º¹¹¹¼¼µ±³´·¸¹¸°š‹†M00.,,00//.-,-./-+,-/0372..--,*,-051-)*PRRWPPRSSTX[RPSRSQROLNPRQPPPNƒÐÌÌʹ¯¼ÉÆÂÃÌÔÖÖÕÒÍÇÅÆÇÍ×ÚÓÆ²š”’˜¢ª±¶»½¿¾¾¾¾ÁÁÁ¿½¹·³©¦£ ¡ ¡¡¡¢ žœœœœœžžž¡¢¢ ŸŸœ˜–“”••”’‘‘’•˜œ¡¦©®²·º¾ÁÂÃÄÄÄÄÃÂÀ¾½¾½»¹·¶µ´´¶·µ³¯ª¢™ˆ„ƒ€€ƒ„ƒ…†††ˆ‰“™¤¨«®´»ÁÅÇÆÃÁÅÇý²¦›””””‘“Ÿª´¶¶¶µ´µ¸½ÁÄÄÀ»µ¨™Š„‡” ¨¯³µ·º½¾½»º¹¹º¼¼´±³µ¸¹ºº²žŠg/31,,11//0/-,...-,,..132/.--+,,-,/3.+*TXSTQQSTWVVXPNRTTQSSPNOPNOQNT0C»ÌÌËŵ±ÀÉÇÂÂËÓÔÕÕÐËÆÃÄÇÌ×ÛÕȶ–’”¥´¸º½½¾¿¾¿ÁÁÀ¿¾¼»·³®«§¤¤¢¢££¡ ¡ ŸŸ žž ¡¢£¢¢¡¡¢¢¢¡ ›˜–••––—˜™š ¡£¥ª¯³¶¸»¾ÀÂÃÃÃÃÁÁ¿¾¼º·¸¶³±¯®®®¬«¨¨¦¡œ–‡€€ƒ…„‚‚…„†‹‘•𤍫¯´ºÀÆÉÇÃÃÄÅÄ¿´§““””’‘—¥°µ¶¶µ´³·½ÁÃÿ¹°¡‘…ƒ‹™¤ª®´µ¹»¼½½»¹¸¸»½¼±°´µ¹º»ºµ¥‹Œ~3*1+,/.////.-,-/-----./01-,,--,,+.030*UXSRSRWXZVVXPOQSTRPQRQQOOQPPY@
}ËÉÌËŲ²ÆËÈÄÀÉÓÕÕÔÐÉÅÃÅÈËÖÜÕʺ¢—“Ž‹˜¡ª¯µ·»½¿¿À¿ÀÀÁÀÀ¾½º¶³¬«§¥¥¤£ žŸŸ Ÿ Ÿ¢£¤¦¦¥¤¥¤¤£¢¡Ÿ›šœœ››œ ¢£¦ªª¬°µº½ÁÁÂÂÂÃÂÂÁ¿¾½º·¶³²³°ªª«ª§¢ ›˜“‡„ƒ‚‚€~ƒ…ˆ’˜›ž¢¦ª¯´»ÁÅÉÈÄÂÆÇž¶©–’“”’• ´¶µ´´³µ»ÀÁÃÄÁ¿¸«›Š‚„‘Ÿ¨ª®³·º»¼¾¼º¸¸¸»½º±²¶¶¸ºº¹¶ªˆŽF"//-.00.-..,-./.-,+-/02/,+,*+,,./47.TSQQSVTRWSUUPPPQPONPQSSSTUSTYM.«ÊÊÌËÀ¯µÆÊÈÅÁÇÓÕÖÔÏÉÄÂÄÇÍÖÚÕ˼¤–”‘Ž‹Œ’¦¬´¸»¼¾¿¿¿ÁÀÀÁÁÀÁ½¹¶±¯±©¨§¦¥¢¡žžŸŸ ¢¡¢££¤¥¤¤¤¤£¤£¡¡ŸŸ¡ Ÿ¡¢¤§ª®±´·¼¿ÃÅÅÄÄÂÂÀÀ¿½º¸¶´±¬ª¨¥¤£¢£ œš˜Šˆ…ƒ‚ƒ€}}~‚†ˆ‹’–› £¥¨ª¯¶»ÂÇÉÉÅÂÅÇÄÀµ¨›—”“““Ž“žª²¶µ´³´¶º¿ÂÄÄÃÀ¼µ¥‘…„—£©«®³¸¼¾¾¾»º¹¶¸½½º¯²¶·¸¹¹¹¸®”†Ž_.,,-/.----,--.-,++,.00,*++*,,,,/42RPMRUSPQSTSRSPPONRSOPQRQRTQQQS)eÉÉËÌʼ·ÆËÈÄÁÅÑÕÖÔÍÇÃÃÄÇÍ×ÛÖÍ¿¨™•’ŒŒ˜¡¨°¶º½¾½¾¾ÀÀÁÁÁÁÁÁ¾º¶µ´±¬«ª©§¥£ ŸŸŸ ¢¡¡¢¢¢ ¡¢£¥¤¤¢ Ÿ ŸŸ ¢¢¥¥¨ª®±µ¹»ÀÃÄÅÆÆÅÄÃÄÂÁ¿½»·µ³±°®©¦¡žš“’‹„}|}„ƒ}~ƒ‡‘–𢤧©¬°µ»ÁÇÊÊÅÂÄÅü²¨œ”“””“’œ§°´¶´³²µ¹¾ÁÂÄÿ¼²žŒƒ‚Žœ¥«¬³¹½¿À¿»¹¸·¸½½¹¯µ··º¼¹º¹±žŠŠv$,-,../...,,,-..-*+-/.++**,-,+++.4SPQUTRVVSSTSTQQRQSQOQNQOQROOOQ=(œÇÇÊËÆ¹«ºÆÊÈÄÀÃÎÒÔÒÌÇÄÃÅÇËÔÚ×Ï«œ–•’Œ“›¤«²¶¹¼¾½¾ÀÀÁÁÀÂÂÁ¿»¹¸¶´´²¯¬¬ª¦¤¡ ¡¡¢¡ Ÿ¡¢££££¢¡ ¢£¡¢¡£¥©¬®²µ¸»¿ÂÄÅÇÇÈÆÅÅÄÃÂÀ½º·´²°®®«§¢›–‘Žˆ†~~……ƒ€„†‹‘•–™œ £¦¨©¬°³º¿ÆÈÉÅÂÃÅû±ªŸ–““”“ŽŽ—£³µµ²²´¹¼ÁÂÂÂÁÀ½¹®š‰ƒ‡•¢¨¬¬²¹½ÀÀ¾¼¹·¶º¾¿¸°³¶·º¼»»ºµ¤Œ…€=).////0..----+++,-.,*,,*,,*,,*,4ƒ‚…ƒ€‚ƒ€€€‚„…‚~|~€€€€€€€€‚ƒ„ƒ€€‚ƒ‚‚‚‚€€€€„†{|}}}~~||}~„ƒ€€~ƒ‚€ƒ‚‚€‚‚€ƒ€€€€‚~€€€€~‚‚}~‚†‚~|~‚~ƒƒ‚‚ƒ‚‚‚€€‚‚€‚ƒ€€‚€€€€€€€‚€€€€€€€€‚‚€€‚‚‚‚ƒƒ‚€€‚ƒ…‚zwy{{zz{zzyx‚ˆ€€€€€€‚‚€€€€€€€‚ƒ‚€€‚ƒ€~€€€€€€€ƒ‚‚„„„}~…~ƒƒ„‚‚ƒ„‚€€€€€ƒƒ„€‚ƒƒ‚€€€€‚‚‚‚‚‚ƒ‚€‚‚€ƒ„ƒ‚‚‚‚‚‚‚‚ƒ‚‚€€‚‚……‚‚€ƒ‡€‚‹ˆ…€€€€€€€€€€€€€€ƒƒ‚€€€€€€€€€ƒ€~…ƒƒ€~ƒ„…………ƒƒ€€ƒ€ƒƒ‚€‚€€€€‚„ƒƒ„„„ƒ€€‚„ƒ‚ƒ„ƒ€‚ƒ€€‚‚€€€‚†…€ƒ„ƒƒ‚€…†ƒ€ƒ‚…†„„ƒ‚€~€€€€€€€€€‚€€‚‚€€€€€€€‚‚€„‚ƒ~€‚€„ƒ‚‚„ƒ‚€„ƒ‚‚‚€‚€€€‚‚‚€‚ƒ„ƒƒ‚€€ƒ‚‚‚€ƒƒ‚‚‚ƒ‚‚€‚ƒ‚€‚ƒƒ‚„‚€ƒ„„ƒƒƒ€€€‚‚wy€…††ƒ‚€€€€€~€‚€€€€€€‚€‚‚‚€€€€~€€€€ƒƒ~€‚ƒ†„‚€€‚ƒƒƒ‚„„ƒ„‚‚‚‚ƒ‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚ƒ……ƒ€€‚‚‚ƒ‚‚ƒƒƒƒ‚‚‚‡ƒ‚‚ƒƒ„„‚€~€…†€~z|‰†‚€€‚‚ƒ€‚€€ƒƒ€€€„ƒ€€‚€~~‚€€€‚‚€€€‚„ƒƒƒ€~€€‚ƒƒ„ƒ‚‚‚ƒ„„„ƒƒƒ„ƒƒ††ƒƒ„ƒ‚‚‚ƒƒƒƒ‚‚„‚‚‚ƒ„ƒ‚ƒƒƒ€‚‚‚€€‚‚ƒ‚ƒ‚‚‰„‚ƒ…‰‰……„ƒ†…€„‡ƒ}†…€‚‚€‚‚€ƒƒ‚‚€‚‚‚„„€€€€‚‚‚„‚€€„†„…„€€ƒ‚ƒ…ƒƒƒ‚‚ƒƒ„€„„ƒ„„‚„ƒ€ƒ‚‚ƒƒ‚€€ƒ‚ƒƒƒƒƒ‚ƒ„ƒ‚‚„„‚‚ƒƒ‚‚І„ˆ…ˆ‡…„ƒ€…‚††„€ˆƒ‚„ƒƒƒ‚€€€€€ƒƒƒ‚ƒƒ€€‚‚‚‚€€‚‚‚€‚‚‚ƒ‚‚‚„‚„„€€‚€€€ƒ†„‚ƒƒ†…ƒ‚‚ƒƒƒ‚‚ƒ„ƒ‚‚ƒ‚‚ƒƒƒƒ‚„„ƒ‚ƒƒ„„‚‚ƒ„…„ƒƒ‚ƒ‚‚‰„€€„‰‚‚ƒ€†‚†„‹‚„ƒ‚‚€€€€€€‚‚€ƒ‚‚‚‚‚‚ƒ‚€€€‚‚ƒ~ƒƒ€€‚…„‚€€…„„ƒ„„ƒ‚‚‚‚ƒƒ‚‚………„…‡†‚ƒ‚‚€€‚ƒ‚‚ƒ‚„…ƒƒƒƒ€‚ƒ‚ƒ…„ƒƒƒƒ‚ƒƒ„‚‚ƒƒ‚ƒƒ‚ƒƒ‚‚‰ƒ„‚‚€}‚‚‚‰ƒ€††ƒ„ƒƒ‚€‚‚ƒƒ€€‚‚ƒƒ‚‚‚‚ƒ„ƒƒ‚‚‚‚‚~€†„‚„‡†„ƒ‚€‚ƒƒ‚ƒ‚ƒƒƒ‚€††…†…ƒ‚„‚‚‚‚‚‚‚„ƒƒ„ƒƒ‚€‚„„ƒƒ‚‚ƒ„…„ƒƒƒƒ„ƒƒ„„ƒƒ„ƒƒ‚‚„…Š„ƒ„„€€ƒ}‚‚ƒ‚‰„ƒ„‚„‰‚ƒƒ‚‚‚€€€€‚ƒƒ€€‚ƒƒƒƒƒƒ„ƒ‚€€€€‚ƒ~ƒ‚‚ƒ……‚‚‚ƒƒ„„‚€€‚ƒ‚‚‚€€„‡‡‡†…„„‚€‚ƒƒ‚‚€‚‚ƒƒ„„ƒ„ƒ€‚‚ƒƒ‚ƒ‚ƒ„ƒ‚‚ƒ‚ƒ„„ƒƒƒƒ‚„ƒ‚…‹„‚ƒ„„ƒ‚ƒƒ‚„„Š…„„‚ˆ‡‚ƒ‚ƒ‚‚€‚ƒ‚‚ƒ‚‚‚‚€€‚‚‚ƒƒƒ‚ƒƒ‚‚‚‚€€‚€€€€„…††ƒ€€ƒ„ƒ†…„€€‚ƒ‚‚„…ƒ€€ƒ†‡…†„ƒ‚ƒ‚„ƒ‚ƒ‚‚‚ƒ„ƒ‚‚ƒ„ƒƒƒ‚ƒƒƒ‚‚‚‚ƒƒƒƒƒƒ‚ƒ„ƒ‚ƒŠƒƒ…†ƒƒƒƒ„„…†Œ„ƒƒƒŠƒ„„‚‚‚‚‚ƒƒ‚‚„„‚ƒƒƒ‚‚ƒ‚‚ƒ„ƒƒƒ‚‚‚‚€€€€€€€€‚††…„‚„…‚†„ƒ‚‚€€ƒ„„‚‚‚„……†ƒ„ƒƒƒ‚‚……‚‚ƒƒ‚‚ƒƒ‚‚‚‚„„„„……„‚ƒƒ‚„…†……„„„„ƒ„Š‚ˆ‡‡‚„ƒ‚‚ƒƒ„„‚†‰…††„„ƒ‚‚‚€€‚‚‚ƒ‚‚‚ƒƒƒ„ƒƒ‚‚ƒ‚‚‚‚€€€€€€€€€‚††…ƒ‚‚ƒƒ„ƒ„…„‚€‚‚€‚ƒƒƒƒ………†‡‡…ƒƒƒ‚‚ƒƒ„‚‚…„‚‚„…„„ƒ‚‚‚‚‚‚ƒ„ƒ„……„ƒ‚‚„„„„ƒƒ„……„„‚„Š€…‡ƒƒƒƒƒƒƒƒ‡Ž…ƒ‚ˆ„‚…†…ƒ‚ƒ‚‚‚€‚‚‚‚‚‚‚ƒ„‚€€‚‚‚€€€‚‚‚‚‚€‚…‡†„„‚‚†ƒ‚‚€„„…ƒ€€€‚‚‚ƒƒƒ……†…ƒ„„‚ƒƒ„ƒ„ƒƒ„„‚‚‚„„ƒƒ‚‚‚ƒƒ‚ƒƒ‚ƒƒ‚ƒ„„„……ƒ„„ƒƒ„„„„„…„ƒ†Š‚…†‚ƒ„„……„„ƒ†Œ„‚…‰ƒ„ƒƒ‚ƒ‚ƒƒ‚€‚‚‚ƒ„„ƒƒ‚‚‚‚‚€‚‚ƒ…„ƒ€„ƒ„ƒ‚‚‚€‚ƒ‚ƒ‚ƒ‚€€€€€ƒ‚‚‚‚……†„ƒ…†…‚ƒ„‚ƒ‚ƒ„ƒ‚ƒƒ‚„„ƒƒƒ„„„ƒƒ„†…„„„„„…„„„…„ƒ„„„„„……ƒƒŠ‡‡†‡‡†…†‡‡†„‡Œ„‰ˆƒ…ƒ‚‚‚‚ƒ‚„„‚€‚‚‚„…„ƒƒƒƒ‚‚‚‚‚€€€€€€‚…ƒƒƒ‚‚€€ƒƒƒ‚ƒ„ƒ€ƒ„ƒ„„ƒ‚ƒ……€‚ƒ€€€€‚‚‚ƒ„ƒ„……ƒƒƒƒƒƒƒ‚‚„…„„„ƒ„„‚‚ƒƒ„„„ƒ…†„„„ƒ‚€€€„‡ˆ†„………„„‚‚†„„………ƒ‚‚€‚€€‚ƒ‚‚‚„ƒ‚‚ƒ‚ƒ„ƒƒ‚€€€‚€€‚„„‚ƒ‚‚€‚‚ƒ‚€€‚‚‚€€‚ƒ‚€€€ƒ‚€€€ƒ„‚€€ƒƒ„…„ƒ„„ƒ„„ƒ‚ƒƒƒ„ƒƒ„„„„‚‚ƒƒƒ„„ƒ‚ƒƒƒƒƒƒƒ„~{zz||}€~}|~€…Š‚ƒƒ„…„‚‚‚€‚‚‚‚‚ƒƒ‚ƒƒƒ‚‚‚‚‚ƒ‚‚€~€‚‚ƒ€€€‚„ƒ‚ƒ‚‚‚€€‚‚‚€€‚ƒ€~€‚ƒ‚ƒ…„„ƒ‚‚„…„ƒ„…„„…ƒƒ……„ƒ‚‚ƒƒ„„…†…„„ƒƒ„ƒ„ƒƒƒ„„ƒƒ„……„„……„ƒ‚zz~€…‡††…†‹„„„„ƒ‚‚ƒƒ‚‚ƒ„„…ƒ‚ƒƒ‚ƒƒ„„„ƒ‚‚‚ƒ‚€€€€€€€€€ƒ††€ƒ‚€€€€€‚ƒ„ƒ„‚€‚€€€€‚‚‚‚ƒ„‚ƒ…‚ƒƒƒƒ„††………„ƒƒ„„„„ƒƒƒƒ‚ƒ„…„ƒƒƒ„††……‚ƒ„„…†………„……„ƒƒ„…„„…„ƒ{{{}‚†…‚„„„ƒƒƒ‚‚‚‚„„……ƒ‚ƒ„ƒ‚‚ƒƒ„ƒƒ‚‚ƒ‚€€€€€€~€‚„‡†ƒ‚€€€‚‚‚€‚ƒ‚€€€~ƒ…„ƒ‚ƒ„„„„„„„ƒ„…„„…„ƒ„ƒ‚‚ƒ‚ƒ„…„‚‚ƒ………††ƒƒ„„„„„††…„‚ƒƒƒ„„ƒ„………„…ƒ‚}{~ƒ………„ƒƒ„ƒƒ‚‚„……„„„„‚‚ƒƒ„„„„ƒ‚€€€€‚‚‚€€ƒ…†ƒ€€€‚€€ƒ€‚‚‚€~€€~€~€}ƒ‚|‚„„„„„„ƒ„ƒƒ„„ƒ‚ƒƒƒƒƒƒƒƒƒƒ„„‚‚„†…„…„„„…†„‚ƒ„……„ƒ„„„„‚ƒ…„ƒ‚‚„…„ƒ„†‡†…„ƒ„ƒƒƒ„„„ƒ„…„…„„„ƒ„ƒƒƒƒƒƒƒƒ‚€€€€€€‚‚€‚ƒ‚ƒ~„……ƒ€€€€‚ƒƒ‚ƒ€…€‚„…‚‚€€€€ƒ€€‚}|‚„……„ƒƒƒƒ„„‚‚ƒƒƒƒ‚ƒ‚„„ƒ‚ƒ‚‚ƒ„„ƒƒ„…„………„…„„„„ƒƒƒƒƒ‚‚‚ƒ……ƒ‚‚„„ƒ„„„…„ƒ‚‚‚‚‚‚‚ƒ„ƒƒ„„ƒ„ƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~€€€‚„ƒ‚€€„ƒ„‚€‚‚ƒƒƒ‚ƒ‚€€ƒ‚‚‚ƒƒ€€‚€‚€€€}}‚ƒƒ„„„ƒ‚‚ƒ„„ƒƒ„ƒƒ‚„ƒƒƒ„„ƒ‚‚„ƒ„ƒ„„„ƒ…††……………ƒ‚ƒƒƒ„„„ƒ„„„„„…„„„„„ƒ„……„„„„„ƒƒ‚‚ƒ„„…„……ƒ„ƒƒ„ƒƒ‚ƒ„ƒ€€€€€€€€‚‚‚‚‚‚}ƒ…‚‚‚‚‚‚ƒ„ƒ‚€€‚ƒƒ€~‚‚€€€€€~~{|}ƒƒ…†…„ƒƒƒƒ„ƒƒ„„„ƒƒ„„…ƒƒ‚‚ƒ„„ƒ„…„„…„‡‹†ƒ„ƒ„„„††……………„„„ƒƒ„……„„……††…†………„„„…ƒ„…„………†…„„…†…„ƒƒƒ‚‚‚ƒƒ‚‚€€‚‚€€€‚‚}„„ƒƒ‚ƒƒƒ…ƒ‚€‚ƒ€‚„ƒ€€€€€€~‚ƒƒ‚ƒ„†‡†…„„…„„„„„ƒƒ……„ƒƒƒ„„ƒƒƒƒ„„…†„‡Š„ƒ‚ƒ„…†Š‹Šˆ„„‰‹Šˆ‡†††‡†„„‡Š‰‰ŠŠ‰‰ŠŒ‰…„ˆ‹Šˆˆ‰ˆ‡„‚ƒ………„ƒ‚‚‚‚‚€€€€€€€…‚€‚‚‚ƒ„ƒ‚ƒ‚‚€‚‚‚€ƒ€€ƒ}}€€‚†‚‚‚„„ƒ‚„†…„„„„ƒ…„„„„ƒ„†…„ƒƒƒƒ‚‚ƒ…„……„…†ƒ„ˆ‰‚}ˆ‰†…‡‰Š‰‰‡‚ˆ‡‚€‚ƒ‡ˆ‚€ƒ„†…ƒƒ‡‹‡ƒ„„„„ƒ‚‚‚‚ƒ‚‚€€€‚€‚~‚€‚ƒ€ƒƒƒ‚€€„…~€|{€€€ƒ€‚„†‚€€€€„…„‚‚ƒƒ„ƒ„…††……„„„„„…„„‚„…ƒ„„…†‡ƒ‚‚…‹Š…|~ƒƒ„‡…}€€€€‰…€…ƒ~z{|}|„€|€~|}ƒŽ‰ƒƒƒ„ƒ‚‚‚ƒƒ€€€€€€€€„‚€‚ƒ……ƒ‚‚‚‚‚€€€{ƒ€~}€€€€€€€€‚ƒƒƒ„€€ƒƒ…†„ƒ‚ƒ……„„…„„„„„……„……„„ƒ„„„„„„…‰„ƒƒ…Œˆ‚|€„………Š…{|}{z{€‚…‚„|~~}{|€€~~„|x|ƒŽ‡„ƒ‚ƒƒƒƒ„‚€€‚‚€€€€€‚€€‚ƒ„…‚ƒƒ‚‚‚‚€sz}}|€~€€€‚‚ƒƒ€ƒƒ{~„ƒ‚ƒ„ƒ‚„„„„„‚ƒƒ„…†…ƒƒ„ƒƒ…„ƒ„†„„„‰†‚†Š‡}|…ƒƒƒƒ‡…‚ƒƒ‚‚ƒ‚„„‰‡‚ƒ‚‚‚ƒ‚~ƒŠ‚ƒ‚{|Љƒƒƒƒƒ€€€€‚‚€€€€€€ƒ…‚‚„ƒ‚‚‚ƒ‚rsx|}}€~€€€~€ƒ‚€~yw‚ƒƒ„ƒƒ‚ƒ…†ƒ‚„„„„††…„„ƒ‚ƒ„………†…„ˆ†„„‚}|…„ƒ‚‚‚‡‚ˆ†ƒƒ…„„„…„…ƒ††ƒƒ‚ƒ„„……€€…†‚……~~ˆ‰ƒ„„ƒ‚€€‚ƒ€€€€€ƒ‚€€‚ƒ…ƒ„„‚€ƒ„‚sqvy|‚‚€~€€~€}}||{z‚ƒ„„…‡‡…„…††…†…„…†††„„„„ƒ„†…††…„†…€~€…†„ƒ„„„„ƒ…ŠƒˆŒˆ‰‰‰‰‡…ƒ†ƒƒ†‰‰ˆˆ‰‰‡ƒ€€…†ƒ„…ƒ„~‰†……‚‚€€€‚‚€€€€~€‚€~€‚„ƒ„„…ƒ‚ƒsqsux€‚‚€€~~~~‚}{}~}~|}ƒ…………†††††‡‡†††‡‡‡†……††…„…„…„ƒƒ‡…€ƒƒŒˆ‚…†……„‰‹…†ˆ††‡ˆƒ‚‡ƒ€‚„„……†‰…€€‡‡ƒ„„†„ƒˆ‚‚„„‚‚‚‚‚‚€‚‚‚€€€€~|~€€€„ƒ€ƒ…ƒ€psuvv{€ƒ|{|}~€||‚‚ƒ„„„„…‡†„„……†‡‡‡……„…„ƒƒ…„ƒ…„„‰„€„ƒƒˆƒ…‡†…ƒ‰‰}~~}}‚‚ˆƒ~}~}~~…ƒ€…‡ˆŠ‹‰…†‚ƒ‚ƒƒ€€€‚‚€€€€€€€€~{{€€€‚€€ƒƒƒ€|osuuuv{€~‚€~{z|}|{~|z€‚„ƒƒ„…†…†‡…„……………†‡‡††„‚‚†‡†…„…‡„€€‚ƒ„„††…„„‚ˆˆ„…ƒ~}€€ƒ…~€€‚€~„‡„‚…‚€‚‚‚‚‚€€€~|~‚ƒ€€ƒƒ‚ƒƒ€}rqrsxwxz€‚~~}|{{}~~~}|z}‚ƒ…„…††‡†††…„„†…„†‡††……††„…†††……†€€‚„„‚‰……††‰‡„†††‡†‡‡††„ƒ…„„…„„„ƒ‚ƒ„€€€‚ƒƒƒ‚‚‚‚‚ƒƒƒ‚‚‚€€€€€€€~~€‚„ƒ‚}}€„…„ƒstsuwttv|ƒ}}~}~}}|{}~|y|ƒ„„…†„ƒ…‡†…‡†…„ƒƒ………………†‡†„ƒƒ…†‡…~€‚†……ƒˆ‰………†ƒ‚„…†‡ˆ‡†‡‡„‚…„„„„…„„ƒƒƒˆ…€ƒ„…ƒ‚‚ƒ‚‚ƒ€€‚ƒ‚€€‚‚ƒ€€~|}}‚‚€~~~~€…„~€uvvtsux{z|~|}}}|~~||||}|y{„†…ƒƒ‚‚…††‡‡†…†‡†………†…„…†…„„ƒƒ…ˆ…ƒ„†ˆ††ƒŠ††„„‡ˆˆˆ‡†…ƒ‚‚‡†…„„…„„ƒ‚„Ї‚„ƒƒ‚ƒƒ‚ƒ‚‚‚‚‚‚‚ƒ€€€€ƒ€||~~{|}~ƒ‚€€€~~€€€ƒ…€|}€vsstw{{}€~}~~~{||}|}~}}~{y‚…ƒ‚„…………†……„………†„‚‚‚‚ƒ„„„„ƒƒ†…‚€‚…†ˆ‡†„ƒ††…‚~ƒ„„…†ˆ†‚€€„†ˆ‰ŠŠ‹‰„‚…‹…‚„ƒ‚‚ƒ‚ƒƒ€‚€€€€€€~~}{{}}{}~€€€‚~~}~{~utuy}€~~}€~||~~~~~}}z‚†„…††††„„…††‡†„††ƒ‚ƒ‚ƒ„…†„„…„……ƒ„††‡†ƒ„„…‚~~~€ƒ†€„‚€ƒ…„‡‹ƒ‚…‰„‚„„„…‚‚ƒ‚€‚€€€€€€€€€€~~|}ƒƒ‚~~}}€~~su{~‚€~~~}~~~}}€~}|}~„†ƒ„‡†…………†‡‡‡‡††„‚‚„…„„‡ˆ‡‡††‡‡†††………ƒ„ƒƒ„……„‚€€†…„…€€€€ƒ‡„‚€„…„ƒ„ƒ€‚‚‚ƒ‚€€€€€€€€€€€}|€„„€€€~„„‚€„ƒvzz|}|}‚}{|}~|~|{}~}~~{‚‡ƒƒ†‡„ƒ„„………†……„‚ƒƒ„„…†††‡†…†‡‡††‡‡‡†…„ƒƒ„„†…„††„„†„‚„…„„ƒ‚€‚€‚„…ƒƒƒ„„……„ƒ€€€€€‚€€€€€€€‚}€€€~}~€‚€€€€ƒ„ƒ‚€€‚†…}}{|€€||y}~}}~}||}}~~z€…ƒƒ„…„„„…‡†„„ƒƒ„ƒƒ„…†††‡ˆ†„„„…„„„ƒ„„„……ƒƒƒ„„„…††ƒƒ„„ƒƒ„……„ƒ‚ƒ„„…„„„„……„ƒƒ„……„‚‚~‚ƒ‚ƒ‚€€‚‚€€}~€}~€ƒ‚‚€€~€‚„~€€‚„„€~‚~|z{~~}}~~~~|}{~ƒ„†††††‡‡†……„„……„…‡††‡‡‡…ƒƒ„……„ƒ‚‚„„…„ƒ„„„…„„„„„…„ƒƒƒ„„„„„„…„„……††…„„……††„‚‚‚‚‚‚‚ƒ„……‚€€€€€€€~|ƒ‚€~~€ƒ‚€€‚ƒƒ~~~~}}~|~}~}}€~}~|}|~{z€‚…†††‡†……†…ƒ„………†……„††…††„†‰Š‡‡‡‡‡…ƒƒ„„„………„„„ƒƒ„„……„„„ƒ…††…„„…†‡†…„„„……„ƒ‚ƒ…‡†‚‚„„ƒƒ„ƒ„ƒ€€~€€€~{{~€€€~€€€€‚ƒ‚‚}}|{|}~|~}€}|{|}}{}}}~}~}|x~‚ƒ‚„„…†……„ƒƒ„„„„„ƒ‚‚ƒƒ†Œ‰‚€ƒ„ƒ…‰‰†„„„…„„ƒ„‰ˆ†„„…„…†…„…†††……†††…ƒƒˆ‡‡…€€€€ƒ„†…„„……„ƒƒ‚‚€€‚€€€~~~}z|~}}~€€‚€€‚ƒƒ‚~|z|~€~||||}|||{|}|||~~||}‚ƒ„„„„„„ƒƒƒ„………„ƒƒ„„„‰‹„€~€€ƒ‚„‹†„„„„…„‡‹Œ†„…„„……ƒ‰Œ††‡…„„„ƒ‚†‚‚…€„€‚€„†„„…„ƒ„ƒ‚€€€‚€€€~{{~}~~|€€€‚€~{|€€}|~}~}|~||~~}}}~}~}~€‚‚ƒ„„ƒ„„ƒƒƒƒ„……„…†…††ŠŒƒƒƒ‚‚††ˆ†„……„ƒ‡Šƒ‚ˆ‡„†††…†„Šˆ‡„…††…†„ƒƒƒ~ƒ†‚ƒƒ‚€‡‡„„…„ƒƒƒ‚€€€€€€‚‚~z~|€~~~€€€‚‚ƒ…ƒƒ}}||z~}||{z~}}||}||~~~}~|}‚‚ƒ‚ƒ„„„ƒ‚‚ƒ„…………††…†‡‡Œƒ}€…‡………ƒƒ…‚„„……„„І„‰‚………†„ƒ‡ƒ…ƒ„…………ƒƒ†‚ƒ€€~††‚‚ƒƒ„ƒƒ‚€€€€€€€|}ƒ€{}}~€‚€€ƒ‚€€„…}~{{}z}€~}€}|~|}~{}~~|}~}|~ƒ…‚ƒ……ƒƒ„ƒƒ„…†††††…„…ˆ‰…†††………ƒ€‚………‡…††€€‰‡…„„…„ƒ…‚‡„†‡†……„‚‚€……‚‚„€€††„ƒ‚„„ƒ‚€€€€€‚‚{{‚€}€€€‚ƒ‚€€ƒ‚z}}}~|||}~~|}}|||{{}€~~~}}‚ƒ‚„„„ƒ„„……„……………†††…‰†€€ƒ…††‡……††……………††Š„ƒ‚ƒ‡‰…„„…„„…†‰ƒ†„„„„„„€€†ˆ‚„ƒ}ˆ†„„„ƒ„„„„‚€€€€‚ƒƒƒ„ƒy{€}€€~~}~€‚‚„……„‚~~~|}}{|~}~~~~}|{}}}€~||~€‚„„„„ƒ…„ƒƒ„„…†‡……††ˆ‡€‚„………………†‡†…†††‰‡…†€ƒ…ƒˆ„„†…ƒ„„†‰ƒ„††…„ƒ‚ƒƒ€„ˆ„…‡‚~ƒˆ…„ƒƒƒƒƒ„„‚‚‚‚ƒƒ…ƒ|z~}}}}~~~~~~~~„‡‡†„€€~~{|}~}||}~€~|}~}~€€€}}~ƒƒƒƒ„„„…„ƒ„„…†………†‡‡†ˆ„„‚‚„…††…†………………Ї€ƒ‚ƒ‚…‚‡ˆ†…ƒ††„‰…†……„„ƒƒƒƒ~€ƒ…Š€ƒŠ†ƒ‚‚ƒ„„ƒ€‚ƒƒ‚ƒ‚|€~}€~~~~~‚‚‚‚ƒ„~~~~}}{~}|}~~}{~}|}~~~~~~z|„ƒ‚‚ƒƒ………„ƒƒƒ„††‡ˆˆ‡„††…ˆ„…†††………‚„…„ˆ‹ƒ‚‚ƒƒ„…‡Š„„ƒ‚†ƒ‚…ƒ…†…ƒƒ‚„„‡‚€~†‡~€‚‹…‚‚ƒ‚‚‚€‚‚‚ƒƒƒ‚€}}~~€€~}~~~€€‚„~||}~~~~€~|}~}~€~}}~~}x|ƒ‚‚„„ƒ……„„ƒ‚ƒƒ„…†††‡†„‰†ˆˆ„‚ƒ„„‡‰„…†‰ƒ€ƒ††‡‡†ƒ‚†„ƒ„„‡…†…„„„ƒ‚ƒƒ„‰†€‚„Œƒ‚‹ƒ‚„„„„‚‚‚€‚ƒ„„ƒ„…„~|}~~}~~€‚ƒ~~€‚ƒƒ‚ƒ|{|~}}~~€€}~~|}~~~}}{|{~y|ƒ‚ƒ„„„„„„ƒ‚ƒ„„„„„†‡†…†ˆ„„ˆˆˆˆˆ‡†Š†„‹†ƒ„††‡‡†‚„ˆƒƒ‚…†…ƒ…†„ƒƒ‚†ˆ‹‚‚ƒ‰‡‚€‚„ƒ„„ƒ„ƒ‚‚‚ƒƒ„ƒƒ„…|}~}~}~‚„~€‚‚‚€‚†…z{|}}|{}~|~}}~}}}~}||~~~~~z~‚ƒƒ„ƒ„………„„„„……„…†‡‡†…„„ƒ†ˆ‡…„…††Š‚ƒƒ‚……„„ƒ€ƒŠ„„‚†„ƒ‚ƒ…ƒ‚„ƒ€†‡Œ‚ƒ€„Šƒ€Œ…ƒ‚ƒƒ„ƒƒ‚‚ƒƒƒ‚‚ƒ‚ƒ„ƒ|~„€~€€~~|~€ƒ~€€€€€€€‚‚ƒ‚|z|~~}}~~}~|~€€~|||}}~}|~€€y~ƒ€ƒƒƒ‚ƒ„…„„………………†††…‡†ƒ„„„‚ƒ„…„‚…Œ‡‚‚‚ƒ‚‚‚…†‡‡„‚†‚ƒ†‡‡…„Šˆ€…„‹†„ƒƒƒ€ƒŠˆ‚ƒƒ„„ƒƒƒƒ„ƒ‚‚‚ƒƒ„„y€€~€€~}|~}€ƒ~~€€‚‚}|{|}}|||}|||~}}~~||}~~~~~zƒ…ƒ„ƒ‚ƒ„„„„„„†……„…………„……†„ƒ„ƒ‚ƒƒ‚„…ˆ„‚‚ƒ……„ƒ„…ƒ‡‡„…ƒ‚ƒƒ‚ƒƒ‚ˆŠ€…ˆ‹…„„ƒƒƒ„~„‹ƒƒ‚„…ƒƒƒƒƒ„ƒƒƒƒ„…ƒ~y}~~~}||}€}|}~}€‚ƒ€|{{}~}||{{|}}}~~}~€~~}~}~~~zzƒ…ƒ‚‚ƒƒƒ„„„„„……„„„„……„……††…††„„……†…ƒ‚‚ƒ„…„„ƒ„…„…„ƒ†„„„„‚ƒ„„ƒƒˆˆ‚†ˆ‡ƒƒ„…„ƒ„„‚…ˆ…‚ƒƒ„ƒ„„„…„…„„„„„ƒ}x~}~~}~€~}~}}~}z{€~|zx€€|{~|~~~}~~}}~~}~~~}}zx‚…‚ƒƒ‚‚‚ƒ„„ƒ„„…………„„„„………†‡‡††‡‡†…†…„ƒƒ„„ƒ„…„„†„ƒ‚ƒƒƒ‚‚‚‚ƒ„„ƒ‚ƒ„„„……ƒƒƒƒ‚‚ƒƒ„„„ƒƒ…„ƒ„…ƒƒ‚}~x|~}}~~}}€~}~€€~|z}~}|{{xuyy||€‚‚||}}}~}||~~~}€~{y‚†‚ƒ‚‚ƒƒƒ„„„„…………„„„…„ƒƒ…††††††…………ƒ„…„ƒ„„……„……„„……„ƒ„„ƒ‚‚‚„„„…„„‡†…ƒƒƒ„ƒƒ„„ƒƒ„………†…ƒƒ„„„„ƒ‚‚x}~~€}~}}~}~~€}zzyyzyyyvuuuvwux|ƒ~{}}~~~}}}~~~}}{y‚†‚‚‚ƒƒƒ‚‚ƒ„……„…††…………………„„……†‡‡†……„„ƒƒƒƒ……ƒ„…„……„ƒ„„„„„„„……………„……„„…ƒ„………††…†‡‡†‡†…………„ƒ‚ƒx}~}~~}}}~~}~}||{{zyyyxwwvuwxzyy{€~}|}~|{||}||||}}~||€~yx‚…‚€€€ƒ‚ƒ„„……ƒ„…„„…†…………………†…………††‡†††„ƒƒƒ‚‚ƒ„„…†††„ƒƒ„……………††††……„„………†„„……………„†‡†…„…†……„„„ƒ‚~ƒ…w~€€~}}}~}}||{yyzywwwvuuxxz{x{‚~}|{}{|}~~}}}}~~€}~€{x……€€‚„………„……„…†……………„……„††……„„…††…ƒ„„‚ƒ‚‚ƒ„……ƒƒƒ„ƒ‚„„„ƒƒ…†…„„………††…„„„…†………„„…………………††„ƒƒ}~ƒƒv}€~~}}}~~~|z|yxyywxxuuvxxyxx{€}|}|}€}~~}}zw‚††‚‚€ƒ„„ƒ„……„††…………†…†…„……†…„„…††††……‚ŠŒƒƒ…ƒˆˆƒ‚ƒ‡†‚‡Š‹Š†‚„„…………„„„„„„†‡††…„„…†‡‡†„„„„„‚‚}}u{~€~~~~}}~}}{{{zxwvxvussux{xuy€~~}~}~}~}}}~~~~~}}}ywƒ…„‚‚€„„„ƒƒ………………………†„„…………††…ƒ„……„…………Љ‡‚„Š€ŒŒŒ„…††……………„„………††……†…„„„…„„„„ƒ~~€‚w|~~€€~€~~}~€~|{xxwvwwusrwwxvvz€€~}|}~|}}}~}~}}|{{}wxƒ……ƒ‚‚„‚€€‚„„„„„„„„„„…†††††…‡†††„„…†„ƒ„„„‡‰„‰ƒ‚‡‰Œ‡‹‰‹‚ŽŽƒ„…ƒƒ„………„…†…††……„„ƒ‚‚ƒƒƒ„„ƒ‚€ƒ„‚}z~~~~~€~~~}~€~€|||yxwwwwuspyxvtuz€€~~{}€~}}{|}|~}||}wzƒƒ†„‚ƒ„ƒ„…„„„ƒ……………††…††††‡†…„…………„„„„…ІƒŠ†€‡‰…Žˆƒ‹†‹‚‚€‡„…„………………†‡††…„„ƒƒƒƒ„……ƒ‚ƒ„‚€„ƒy|~}~~€}}€~~~~z{|zxxwwvsozyxvvz‚|z||}€~~€~||~~~w{ƒ…‡ƒƒƒ‚…†‚„„„……†…„………†…„„„„…‡………††…„ƒƒ‚†‰„ƒ‡ˆ„†ˆ†‹†ˆ‚„Š‚€…އ„„„„„„„„…†…„„„„„ƒ‚ƒ„†††„ƒ‚€€€‚‚‚{{~}}}€€~}}€{|~zyzzvsrryxxyyz€}||{}~}|}~{|~~}}x{‚…‡„ƒ‚‚ƒ„ƒƒ‚‚ƒ„…†………†††…„„„„„††…„……††††…Šˆ††‡‡‡‚†‰„‚ˆ‡ˆ„„Œˆ†‹Ž‰„„†…„ƒƒ„„„„…„…†…††…ƒ…†‡††„„~…ƒ‚‚ƒ{{~~~}}~~€~~~}zzz{ywvtutxxwwvz~z|}~|}}{|}}|}‚€‚€y}‚ƒ…„„‚‚ƒ‚‚ƒ€ƒ„„„„„……†††…„„„„†‡‡…†…‡††‡ˆˆ……ƒƒ†…„‡‡„ƒ„†…„‰‰‹Žˆ‚„ˆˆ††……††…„„„…„ƒ„…„„…‡†„ƒ„ƒ€€‚„…„‡}|~~}€~}}~~}~}|zxxxvvusyxwwwz}€||}~~~|}}|{|~‚}w}ƒ‚…‡†…„„ƒƒ‚€‚„„ƒƒ„„†‡††††………†††…………………†……††……„„„…„††„ƒƒƒ‚ƒ……„„†‡†‡‡‡‡††…„„ƒƒ„……………†……„ƒ€€ƒ…ƒ„{|~}€€}~}~~~~}zzyzzxvvtxxxwwy|~~~~~~}~}|~}}||}~~~w|ƒ„†‡……††……ƒ„ƒƒ…„„„…††…††…„„„……††††‡‡†……†‡ˆˆ‡‡‡‡††††ˆ‡††…„…†…†‡ˆ‡‡ˆ‡‡‡‡††‡†…ƒƒ„…†‡‡†…………ƒ€‚ƒƒƒ„‚€{|€~~~€€~~}~€{y{yyxwvvvwxxuuv~€€~}}~||}|}}€~~€€}w}„†……ƒ„†„„ƒ„„‚„…„„„……„…………„„…†††‡ˆˆ‡‡‡†‡‡ˆˆ‡ˆˆ‡‡†ˆˆŠ‰‡†††‡‡†‡‡ˆˆ‡†††††……†…ƒƒƒƒ……†‡††……„‚€€…ƒ†y}€~~}€€~~~€~|zxxxxxwvxvuussz~}}~~~~~|}~€€}}}|u}„„„„ƒƒ„†…„ƒƒƒ‚ƒ„……„„……„††††…„…†…†‡‡†„„„ƒƒ‚ƒ„„„……†††‡‡‡††‡………„…†‡†……††††…„„„ƒ„…‡†††……††…‚ƒ…ƒƒ……„€ƒƒxz~~}}|~~€{~}zyxxxvsuttwus{}~~}~~}|~~|}}|~€|w„…„…ƒ„„„ƒƒ…„ƒƒ‚‚ƒ„…††††‡†‡‡†‡‡†……†„ƒƒ„„ƒƒ‚ƒ‚ƒ…†…„…‡…„ƒƒƒ‚‚‚‚„……†…„„…†„……†††††††††…ƒ€ƒ†„„†…‚ƒ…zx}}€€}~~}~~~}€‚}{|{zywvurvutvwxy€~}|~~~€~}~}~|~}w……ƒ‚„„……ƒƒ„…ƒ‚‚ƒ‚ƒ„……†††‡‡††††…„„………ŠˆŠŠ‰‰‰ŠŠ†‚„†…„„†…ƒ‰‹Šˆ†ˆŠ†‚ƒ„„„ƒƒ‚…‡††††…†…††…ƒ€…„ƒ‚„…ƒ‚€…ƒzz|~~~~€}}~~€ƒ}x{|zyywttvuvrswz~~}||||~€€}~~~~~w~„ƒƒƒ„„†…„†…„ƒ‚ƒ…†…ƒ„………††††††††††………‡…„‡‡‰‰‡†‡‹„‚„ƒ…„ƒ‰Œˆ†…††…†‰‹…‚„„„…„„††‡†…„……………„‚ƒ„„„„………‚‡‚z{}}€~}}}~}}€~‚}{z}|zyyvurqtvttww|}~~~|}~}~~}~~€~x„„„……ƒ„…„…„„‚‚…‡†…„ƒ„„……„…†‡‡††††…††ˆ„ƒƒ„„…ˆŒ……ˆ‚ƒƒƒ‚ˆŠ†‡‰‰Š‹Œ‰…‡Œ†ƒƒ„††††…†…„…††„„„‚ƒ€‚ƒ„†…„…€y{€}€~|~}}~~€‚€}{zxvxuuuwvvvv||~~~~~~~€€~~~}w……„…‡…‚„††…„ƒƒ„…†…†„‚„………………†……††††‡ˆ…ƒ€‚ƒ‚†ˆ„‡ˆƒ…ƒ…†‰Šƒƒ„ƒ†‰„‡‹‡†……„…‡†…„…†††„…ƒ€‚ƒƒ‚ƒ…†„€„†€y{~~~~€~|€€€~||zyywtwyyxwxz~~~€~~€€€€}}~~~}v€„ƒƒƒ……ƒƒ„…††††„‚‚ƒ…„ƒ„„…†……†…„††††‡‡„†€ƒ…†…„‡Š†Š„„…‡ˆ‰‚ƒ…†……ƒƒ…„ˆ‰††„ƒ…‡†„„…††……„‚‚ƒ„ƒƒ„„ƒ‚……y|}~~~~~€€}}~€~}}~~{zxzzvqvxyxxwuz~}~~~~}~~~~~z€‚‚ƒ„„„„ƒ„…††…„ƒ‚ƒƒ„„„„„………‡‡……†………ˆŠ†ˆƒ†‡††…„‰…‡ƒˆ†ƒŠ„ƒ†……„„…ƒ„ƒ…ˆ………………†„„…………„‚€‚‚„„ƒ‚ˆƒy{|}}}~}}~~}~}}}{|zyyxvyyxyzywz|}~~~~~~~~~}~~€z~ƒ„ƒ„…ƒ‚ƒ„†„„‚‚ƒ„„…†††„ƒ„„…†……††††…‡‹†ˆƒ…†………ƒ‡ƒ††€‰„†Š…………„„„ƒƒ„„…Š„…‡†††‡†…†…„„‚€ƒ„ƒ‚‚„„„„…„†‚€x{~~}~€€~~}}}}{y|yx{zyyyz{{}~}~~~~€}}}~€€z€‚‚ƒƒ„†„„„„……†………†……„„††„‚„…†††††„‡‹†ˆ‚……„„„ƒ…€…ˆ‚‡„ˆ‡…‡‡……†…ƒƒƒ†‹„………‡†‡†‡…ƒƒ‚€ƒ……„„…†…‡…ƒ††ƒ~v}~|}}~}~~~€}{}}~}|zx|{xyyz|zxxy}~}~}}~}~}{~~~~}{€‚‚‚ƒƒ††„„„„…†‡†………„ƒ………ƒ„„……††††„‡‡†‡‚„„„ƒƒƒ‡‚†ˆƒ‡„‡…‚‡†……†…„„€„ˆ‰…††‡‡‡‡††…„ƒƒ„„‚ƒ‚„……„„„‚‡†„{~~~~|~~~}}~~€|}}}}|z|zywwwyzxxz~}}}~~~~~}€z|€ƒ„„ƒ†„‚‚ƒ…††„„†……„††ƒ„†…ƒ„„„……††…‰ˆ…ˆ‚„„„„ƒƒ†‡…†‡†ˆ‚…††………†‚ƒ†‹ˆ…††‡‡‡†…„„„‚‚„„‚„ƒ„…„…ƒ€…„„y€€€€~}}~~}‚|}}~}~€~{}uuwyxxx{}|}}}|}~}|€~~~~~~~{ƒƒ…„„„……„„ƒƒ„„„…†‡†††…††‡†……ƒƒ„ƒ„…†…‹‰†ˆ‚„…„„„‡…„ˆ‚…‰†Šƒ‚…………„‚††Ž…„†††…†……„ƒƒ‚‚„…„ƒƒ„„„…‡…‚€„††ƒz€€~~~~}}~}~€‚~}}}}}}}~}}xvxyyzyx{|~|€€~~~}€~|~}{ƒ„ƒƒƒƒ„…†…„ƒ„„„„„…………†……‡‡‡‡‡…ƒ„„………ƒ‹††‚€ƒƒƒƒ‡‡ˆ‰ƒƒ„‰††‡‚ƒ„„„€€…‡‹Šƒ…†……………„‚ƒ‚‚ƒ………ƒ„…„„…†„ƒ‚‡‡†ƒ{}€~}~~~||~€€~~~|zy|~{yxxz{zz|}|}}~~~~}~€€}|}z}…ƒ‚‚‚ƒƒ„…†„‚ƒƒƒƒ…………†‡†…‡‡‡‡……†……………ƒŠ„ƒƒ„‚ƒ†‡„…Š……†…ˆŠ‚‡‡„„ƒƒƒ††‹ƒ„†‡‡‡†…„ƒ‚ƒƒƒ„††……„††††…ƒ…ˆ†…ƒ€~~}€€‚€}|~€€€~~}{yyz{xy}€|{zxyyy}~|~€~~}}}||}}~z~…ƒ‚‚‚ƒƒ„††…ƒ‚„…………††…†††„ƒ‚…‡‡…………ƒ‰…„…‡†…†††‹‹„…†††…ˆˆ„…‡†………†Š‡„†‡‡‡†‡„‚‚ƒ…„„……††……………„ƒ†„„‚€}~~€€€~||}€€~||{zy{}zz{yzzxvyxx{}|~|}~}~}}}}zz…„ƒƒ‚‚‚ƒ…††…ƒ‚ƒƒ……„„„……†…„……†‡†„„††„Љˆ‡‡‡‡‡ˆŠˆ…„†‡‡†…ƒ‡Š‡„ƒƒ„…Žˆ†‡‡‡††……„‚ƒ‚„„ƒƒ‚ƒ†…††……†ƒ‚…„„‚€‚€€~}€}~~~}~}~~~}||||}|{}|{yx}}yz||}}~~~~|}{{ƒ‡‡ƒ‚‚ƒ„„††„ƒƒƒƒ„……„ƒ„………†ˆ‰‡ˆ‡…„†‡††‡‡†‡ˆˆ‡‡†……‡‡‡‡‡††………ˆŠŠ‹Œ‹Š‡†‡‡†††…„ƒƒ‚„„‚ƒ„……„………†‡†…„†…„„„‚ƒ}}|~~~~‚„‚€~||~{z€{|{{yz|}zy~}~€~~€~~€y|„…†ƒƒƒƒ„„†……„‚‚ƒƒ‚‚‚„…†‰‰‰‰‡‡††‡†††††‡‡…ƒ„„…†‡†………††††…„„………ƒ„†††††††„ƒƒ‚€‚…†††„„…………„€„ˆ…„ƒ‚„„}~~€‚}}€‚ƒ€€~|}||||{{{~|{{zyyzzzz~}~~}|~|~~||}y~‚„…‚‚„ƒƒ„„„…††…‚‚‚‚…‡‚„………‡‡ˆˆ‡ˆˆˆ‡††‡‰Šˆ‡‡††‡‡†…„…††……†††…††††‡†††…„„ƒ‚~}€ƒ…ƒƒ……„………‚†‡†„ƒ‚„ƒ~‚~~~}~€€~~€~||}}zy{||{{|yzxxz{yz|~~}}~}~~}~~}}€|z€€…†‚‚ƒ„„„„…††………ƒƒ‚‚ƒ‰††…„„„ƒ‚ƒ‡‡ˆˆˆ‡‡‡ˆˆ‡‡†††………†…††‡†††‡†††ˆ‡††††„‚‚‚~~‚‚ƒ…„„„ƒ…†…„„„……†……ƒ…„ƒ€}~~~}~~}~€~}}~||}|{z|||}}|}~{}}~{}~}€}~€~z|„ƒ„†‚‚ƒ„……„†‡‡†‡…ƒ‚„ƒ„ˆ€€‚ƒ„‚„‰††„‚…†‡†††‡‡††…„……………†††‡‡‡††‡††…„ƒ„……„‚‚ƒ|…………„Œ‰……‡‡…„„„„‚†††…„„„‚€}€~}~~~~~~€~~~}|z|}}z||~~~}{|}}€~~~}~‚}}x}‚„„†‚‚ƒƒ„„ƒ„‡‡†‡„‚‚ƒ†‡~}‚ƒ„‹‡†ˆ…‚…†‡‡†…ƒƒ‚‚††…†‡††‡‡††‡‡…ƒ€~€‚‚„„€ƒˆ‰ƒ~†ˆˆ‡‰‹Ž…ƒ…††…„„ƒ…‡…„…„‚ƒ‚€|€~}}~~~}}~~~~}}}}~~~|{}}}~€}}}€~~~~~}}{€…ƒ‚„‚„„„„„…†…†‡…‚ƒ‚ƒˆ…~~|ƒƒŠˆƒ…„„…†††‡„‚‡‡‡„„ƒ„††††††…ƒ€‚„„ˆ‡‡‰‹‰„‚€…††‰……††‰ˆ„„ƒ……„„…‚~‚††…ƒ‚‚‚……ƒƒ{€}~€‚€}|€~~€||}}~}}€}~|}|~~}~}~}{z„ƒ††‚‚ƒ„ƒƒƒ„†††††………„…ƒ‚‚~‡Šƒƒ†‚„…††††ƒŠ††ƒ‚…†‡‡††‡„€…†‘‘ŒŠ„Š‹€…{‰†…†…„„ƒ„„…††…„„‚‚„††…„ƒ‚…„‚€€}~€~}~}}}~~~}}}}|||}}|||~~~}~}||~~}€}|~xz‚ƒ„††ƒƒƒƒ„„„„†‡†††‡†„„†ƒ‡‚€„‰„‚…ƒ………„„ŠŠƒ‚„†‚‡‡‡†‡†‚ˆ‹‹ˆ†……Љ€‚†|†‰‡‡††…„ƒƒ„†…„……„‚ƒ…††…„„‚ƒ„‚€€‚~|}}~~€€€€||~{|}|{|}€~~~{}~}|~}€~€~}~|‚ƒƒ„…ƒ‚ƒƒ„„ƒƒ„…†††‡…„ƒ……}†„}ƒ†„…€ƒƒ…†…‚ˆ‰‡ŒŠ„ˆ…†††‡„‚ŠŒŒ„‚„„„„„ŠŠ‚‚†‡€‰†…†……„ƒƒ„………††…ƒƒ…‡…„ƒ„„ƒ††„‚ƒ~€~~€€€€~~}{}~~~~€~|~~}€€}}~~~}||}‚„„„„†ƒ‚ƒƒ„ƒƒ„„…†††‡††‡‡††‚ƒ„…„ƒƒ„†ƒ†‹…І‹†††„††‡†ƒ‹‰‰ƒ„„…†…„ƒƒƒƒˆ‡€Š‡†…ƒƒƒƒƒ„„……………ƒ…‡†„ƒƒ„ƒ„…„„„ƒ‚~~€~€€~{~~€€~~|€€~~~€‚€€~~|}~~~{|z}„„ƒƒ…ƒ‚ƒƒ„„…„…†„„„„…†‡‡ˆ€|‡…€„„‰ƒ€„„„„ƒ‰…ˆ…€ˆˆ‚‰ƒ„††„†‡†Š‚„……†„ƒ„‚€‚‚„‰„|‰‰……‚ƒ…„ƒ†‡†……„ƒ‚„††„„……‚€„…ƒ‚ƒƒ‚}‚€~~}€|€€€~}~~~~€‚€}}ƒ~€~€~}~}zz€„…„„††„„ƒ„„„ƒ„…„„ƒƒ„‡‡ˆ‹„~…‡„…ˆ‹ƒ„„ƒ‚ƒˆ†ƒŠ…ˆˆ…ƒ„‚‡…„ˆ‚„…„ƒƒ€€ƒ‚ƒ‡…~ƒˆ‡‚„‚ƒ…‚‚ƒƒ„„…‚„‡†„„„„‚€„„‚ƒƒ„ƒ~‚ƒ~‚€€€€€€€||}}|~‚€~~}}}~~~~~}}€‚ƒƒƒ„…ƒƒ„„„„ƒƒ…††„„…††‡‰„…‡‡†ƒ…‡‚ƒ„†„…‡ƒƒƒ€‚†„…†‚ƒ†ƒ…†ƒƒ„„ƒ…ˆ…„‚ƒˆ‡Š€„…ˆ€€ƒ„‚|‰Œ…„…„€ƒ„…†„…†„€„ƒ„…ƒƒ‚‚~~€€€€~~~~}|{|}‚ƒ€€€€€„…€}~~}}~|}‚ƒƒƒ„„…ƒ‚ƒ„„…†…††……†……„„ˆ‡‚ƒ‡Œ‰‚…ˆ‚„†„†…€€|~€€…‡‚ƒ‚‰„ƒ†‚ƒ††ƒ…‰†ƒ€ˆ€€†‰‚†ˆ†‰€ƒ’Œ„„ƒ‚„………„†‡„€„†„„„ƒ‚€~~‚€}}|{|}~||{{€ƒ‚€ƒ‚†|~~~}|~€ƒƒƒ„„„†„‚„„„………………†††…ƒƒŠ‹ƒ„‡‰ˆ†‡‰‚€ƒ„ƒ…„€€‚ƒ„‚…„ˆ‡€…ƒ††„†‹††„Š€‡Š„‚‰ˆ††“ƒ……‚„…††……‡†ƒƒ†‡††…„„ƒ€~‚ƒ~}|||~€€~~~~~‚‚€‚‚…~~~€~|€‚€„ƒƒ„„ƒƒ……ƒ‚‚ƒ…………†††……„ƒ‡Š„…‡………‡‰„ƒ…†ˆ†‚ˆ‡‡ˆ‡†……ƒ„‚„…†‰‚ƒƒ…††‡‡„‡‡Š…‚ˆ‰‚‚ƒ‹…‚€‚”…ƒ…ƒ‚ƒ†††………†„‚ƒ††„„„‚ƒ„ƒ€€€~€~~}~€€€}|}~}€ƒ‚€€~…ƒ}}|}}‚€„„ƒƒƒ„ƒ„…‚‚‚ƒ„„………††„„„„…„ƒ…………ƒ†‰„‚ƒ„†ƒ†ˆ‡Š‹Š‰ŠŠˆƒ‚‚††‰‹ƒƒƒ‚€€€ƒƒ†‰…„ˆˆƒ…ƒŒ‹ˆ‰Ž†…‡…‚„†……………††‚€‚‡†„…‡…‚‚ƒ‚€€‚€~~~~€~}}~{|~‚€€€€„ƒ{|~‚€‚ƒƒ„‚ƒ„„ƒ†‡ƒ‚ƒƒƒƒ…††††…„……††††…†‡…†Œ‰‡…‰ŠƒŠ†††‡‡†……‡†„‡‡Œ‹ƒƒƒ}~€ƒ‰„„†‹…„ƒ‚„ˆŠ‹‰„„†…‚„……‡†…………‚ƒ„…„………ƒ‚‚…ƒ€~~|~€€}~~{{~~€~}€€ƒ‚€€‚€…}{}ƒ……ƒ‚„ƒ„„…‡„‚‚ƒ„„……†‡…„………†‡††…………†ˆ‰ˆ†ŠŽŽŒˆ††‡‡‡‡‡†Š‹ŠŠ„†‡‡‰Œ…ƒ€‚ƒ‹€„ƒƒ……ƒ‚„††…†…‚ƒ…‡‡††……‚……†††…„„ƒ‚‚€ƒ…ƒƒ‚~€ƒ€€€€€}}€~}|~‚‚€€‚~}~}€…„‚‚‚ƒ……‡‡„‚‚ƒ„……„„…‡††……„„†„„…„„…†††……„…†‡‡‡‡‡†…†††‡ŠŒ‡†††…†Œ‹††‡‰‹ˆƒ‚ƒ†‹‡ƒ„„…„…‡ˆ†…‡„‚…„…………†††‚††…„„„ƒƒƒƒ„ƒ€‚‚‚~€€~‚ƒ~}}~‚€€ƒ€~‚„ƒ‚……‚‚„„…‡†„‚ƒ„ƒ„ƒƒ„…†‡‡……†‡…„…„„…†††††††………†…„„†‡‡‡††††††††‡††ˆ‹‹Œ‹‰„‚…†…ƒ‚ƒ„………†……††…€……†……„„…†„‚ƒ†‡†…„ƒƒƒƒ„†ƒ‚‚‚‚€~||}~~}~|„ƒ‚€€~ƒ‚‚~|€„…ƒƒƒƒ„…„„‡‡†ƒ‚ƒƒ‚‚‚ƒ…††……………„„„„…††……†‡†…„„ƒ„„…†‡††‡‡‡ˆˆ‡‡†††††‡†…ƒƒƒƒ„…„„ƒ……„„…†‡………„€„…†‡†„‚ƒ…†ƒ‚„††……„„ƒ‚‚ƒ„ƒƒ‚‚ƒ|{|‚~zz}{|}€€ƒ‚€€€|~€„€~~…„ƒƒ„„„ƒƒˆˆ†ƒ€ƒ…ƒ‚‚„…†…„ƒ„……„„…†…„„„………„„ƒ„†‡‡‡‡††ˆ‡†††……„ƒƒ„„…†…ƒƒ„„…†…„ƒ…††…†‡†††…‚€ƒ„…†…„………„ƒ‚…‡†„„„„ƒ‚‚ƒ…„‚„€{{|~€€~||€|{z€„€}€‚‚€€€„€€„†„„‚‚ƒ„„ƒƒ†‡ˆ‡„ƒ‚ƒ……ƒ„„…………„„„„ƒƒ„…„……†††„„……„ƒ…‡‡††††‡†„„„…„ƒ‚ƒƒƒ‚‚‚„………„„„„†‡†††††††…ƒƒ„ƒ…‡†…†‡‡…„ƒ‚…†„‚ƒƒƒ„„…„…†„ƒ€ƒ„€€}{~~}|~~}}~{€€‚€‚€~€€€…ƒ€‚…‡…ƒ„„ƒ‚…†‡††…‚„…„„„…………††††„ƒ„„„„„„„……„„…………††††††‡†……„„…………„ƒƒƒ…††……„„†‡‡††††††‡†€„„„†‡†…†††…„€……ƒ„…ƒ„††…………„ƒ…€~€}~~{z~}{{~~ƒ€~€€€~~~†ƒ€ƒ„„‚‚ƒ„…„ƒ‚ƒ…‡†‡†‚ƒ„ƒ‚ƒƒ„„…‡‡‡‡‡†‡†…………†…„„„…‡†‡‡‡‡‡†………†„‚„„‚‚ƒƒƒƒ…‡†……†……‡††…‡‡‡††„‚‚ƒ„…‡‡…„††…‡†ƒƒ‰‡„„…„ƒ„…„„†‡…ƒ‚€„€€€~~~~|~‚€€ŒŒŒ‹‹‰‰‰ˆ‰‰‡‡‡ˆ‰ˆ‡ˆˆˆ‡‡ˆ‰‰Š‰‡‡ˆ‡ˆ‰ŠŠ‰‰‰‰ˆ‡ˆˆˆˆ‡‡‡‡‡ˆˆˆ‡‡‡‡†††††††††††…†…„ƒƒ‡‰†††ˆ‰‰ˆ‡‡†‚‚„††……………†………†††‡‡……‡‡‡†‡‡‡‡‡‡‡ˆˆˆˆ‡ˆˆ‡‡ˆ‰Š‹Šˆˆˆˆ‹Šˆˆˆ‡Š‰‡‡ˆ‰‹ˆ‡‰Š‰‹‰ŽŒŒŒŒŒŠ‰‰ˆ‰Š‰ˆˆ‡‡‡‡‡‰‰ˆ‡‰‰‰ˆˆ‰‰‰‰ˆ‰ŠˆˆŠŠ‰‰‰‰‡‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡††………†……†††…………ƒ‡Š‰‰ŠŠ‰‰‰Š‰‡Š…€„………„„„…†††…††‡‡†…††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰ˆ‰‰ˆˆ‡‡ˆˆ‰ˆ‡ˆ‡ˆ‰‰‡ˆ‰ŠŠ‡‡ˆˆ‰‹ˆ‰ŽŽŒŒ‹‹‹‹ŠˆˆŠ‹ŠŠŠˆˆˆ‡ˆ‡‡‡‰‰Šˆ‡‰ŠŠ‰‰‰‰ˆˆ‰‰‰ˆˆˆˆ‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡‡…………†††………††…„‚…†………„„„„„„‚„„€ƒƒ„„„„„……†††††……†††††‡‡‡‡‡‡‡‡‡‡‡‡ˆ‡‡ˆ‰ˆ‰‰‰‰ˆ‡ˆ‰‰ˆ‡ˆ‡‰‰‡†ˆŠ‹Š‡‡‡‰Š‰ˆ‰Œ‹Œ‹ŠŠŠ‰ˆ‡ˆŠ‹Šˆ‡ˆ‹Šˆ‡‡‡ˆˆˆˆˆˆ‰‰‰‰Š‰ˆ‰‰‰ˆ‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡‡‡††††………††††…††…„„ƒ„†††…„„„…„†…ƒƒƒ‡‚‚„…„…††……………„…†‡††‡†††‡‡†††‡‡‡‡‡†‡‡ˆ‰ŠŠŠ‰ˆ‡ˆ‰ˆˆ‰‰ˆ‰ˆ‡ˆ‰ŠŠ‰ˆˆ‡ˆŠ‰ˆˆŠŠŠ‹‹ŠŠ‰ˆ‰‰ˆˆ‰ˆ‡‡‡‰‰‡†‡‡ˆˆ‡‡ˆˆˆ‰‰ˆ‰‰ˆˆ‡‡‡‡ˆ‡‡†‡‡‡‡‡†††‡††††††††……†††…†…„„……„………„„„„„†ˆ‡†ƒ„‹‡‚‚‚‚„…†††††………††…††…†‡‡‡‡‡‡†‡‡†‡‡ˆˆ‰Šˆ‡‡ˆˆˆˆˆˆ‰‰Šˆ‰ˆ‡ˆ‰‰Šˆ‡ˆ‡ˆŠ‰ˆ‰ˆ‰‰‰Š‹ŠŠ‰‰‰ˆ‡‡‡‡‡‡ˆ‰ˆ‡‡‡ˆ‡†‡ˆˆˆ‰Š‰ˆˆˆˆ‡‡ˆ‰ˆˆ‡†‡‡‡‡‡†…†††…††…††‡†„„„…………„„ƒ„…„……„ƒ‚…†††„‚„†‡Š‰‚„………†††…†††‡……†††‡‡‡‡††‡‡‡‡ˆˆ‡‰ŠŠˆ‡ˆˆˆˆ‡‡‡ˆ‰Š‰ˆˆˆ‡ˆ‹‰ˆˆˆˆ‰ŠŠ‰‹‹‹Š‰‹Œ‰‰Š‰Š‰‡‡‡‡ˆˆ‡‡ˆˆ‡‡‡‡‡‡ˆˆˆ‰‰‡‡‡ˆˆ‡‡ˆˆˆˆˆˆ‡†††‡‡‡‡‡†‡††……………„„„………„„„‚‚………„ƒ€}~€‚ƒƒ…ƒ‚„ƒƒ†„ƒ„„„„………„„……………†††‡†††††††††‡ˆ‡ˆˆ‡‡‡‡ˆˆ‡‡‡‡‡ˆˆ‰‡†ˆ‰Šˆ‡‡‡ˆŠ‰ˆŠŠŒ‹‹ŠŠŠˆˆ‰‰‰‰ˆ‰ˆ‡‡ˆ‰‡‡‡‡‡ˆ‡‡‡ˆˆˆˆˆ‡‡‡‡‡ˆˆˆ‡‡‡ˆˆ‡††‡‡††††…†………„„„„„„„„„„„„„‚†…„„~~‚ƒƒ„†„ƒƒƒƒ‚‚……†………„„„„„……………………††††‡†…††‡ˆˆˆ‡‡†ˆˆ‡ˆ†‡‡‡‡ˆ‡‡‡†ˆ‰Š‹ˆ‡†‡‡Š‰‰‰ŠŒ‹Š‰‰‰ˆ‰Š‰‰‰ˆˆˆˆ‡‡‰ˆ††††‡ˆˆ‡‡‡‡ˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡†††………„…………„„„„„„…„„„ƒƒƒƒ„‚€†„ƒ„„€€ƒ‚‚ƒ„ƒ„ˆˆ„ƒƒ„„………………„„„„††„„…………„…†‡‡††††‡‡‡‡‡††‡ˆ‡‡ˆ‡‡†‡ˆ†‡ˆˆ‰Š‰ˆ‡‡‡ˆˆˆ‰‰‰ŽŒ‹Š‰‰‰‰‰‰‰ˆ‡‡‡‡‡††‡…………†‡‡‡‡‡‡ˆ‰‡†‡†‡‡‡‡‡‡‡‡‡†††††………………„„„„„„„ƒƒƒ„„„„„„‚‚ƒƒƒ††‚……ƒ‚‚€ƒ…‡…ƒƒƒ„„ƒƒ„„„ƒƒ„„……„„…………………†††…†‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆŠŠ‡†‡ˆˆˆ‡‡‡ˆ‰Š‰‹ŠŠ‰ˆˆˆˆ‡ˆ‰‡††‡‡†…†……†‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡†‡‡‡‡‡‡‡†…„„„ƒ„……„„„„ƒƒƒ„„„ƒƒ„ƒ‚ƒƒƒ„†„†…‚‚ƒƒ€€ƒƒƒƒ‚„„„„„ƒ„ƒƒƒ„„„„„„„…………„„„………†‡‡‡‡‡‡‡†‡‡‡‡‡†ˆˆˆ‡‡‡‡‡‡Šˆ††ˆˆˆˆˆˆˆˆŠŒŠ‹‹ŠŠŠˆ‰Šˆˆˆˆ‡‡‡ˆ‡‡‡‡ˆ‡†‡††‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡‡††‡††…„………„ƒƒƒƒ„…„ƒƒƒ„„„„„ƒ‚ƒƒƒƒƒƒƒƒ…ƒ‚‚‚„‚€‚‚ƒƒƒ„ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„……„„…………††‡†‡‡‡‡†††‡‡‡‡‡†‡‡‡‡‡ˆ‰ˆˆ‰‡††‡ˆ‰‰ˆˆˆ‰‰‹Œ‹Š‰‰‰Š‰ˆ‡‡‡‡ˆ‰ˆ†‡‡‡†‡‡‡‡‡‡‡‡‡‡ˆ††‡††††††‡‡†………………„„ƒƒƒƒ„„„ƒƒƒ„„ƒƒƒƒƒ‚‚‚„‚‚‚‚‚ƒƒ‚‚‚…ƒ€‚‚‚ƒ„ƒ‚ƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„………†‡‡‡‡‡‡†…†‡††‡‡‡‡‡‡‡‡‡…‡‡ˆ‹Š‡…†‡‡‰‰‰‰ˆˆˆ‹Œ‹Šˆˆ‡ˆ‡††‡‡‡‡ˆ‡††‡†…†‡‡‡‡‡‡‡‡‡‡†††‡‡‡†††††††††……†…„„ƒ„„„„ƒ‚ƒƒ‚‚‚ƒƒƒƒƒƒƒ‚‚‚„ƒƒ„ƒ„ƒ‚ƒƒƒƒ„„„ƒƒƒƒƒ„„„ƒƒƒ„………††††††‡†……†‡†‡‡††‡‡‡‡‡†††‰‰‡‡‡‡‡‡‡‡‡ˆ‡ˆ‰ŠŠ‹Šˆˆ‡‡‡ˆˆˆˆ‡‡‡††…‡‡†‡‡‡‡‡‡††‡‡‡‡††‡‡‡†††††††…††……„ƒƒ„„…„ƒ‚‚ƒƒ„ƒ‚ƒƒƒƒƒ‚€ƒ„…„‚‚‚‚‚€‚ƒƒ‚‚ƒ„ƒ‚„ƒƒ‚‚ƒƒƒ„„„ƒƒƒƒƒ„…††††…†‡‡‡‡‡‡‡‡†††‡‡‡‡‡†††ˆˆ……†‡‡ˆ‰ˆ‡‡ˆ‰Š‹ŠŠŒ‹ŠŠŠ‰‰‰ˆˆ‡ˆˆ‡‡‡‡‡‡‡‡†††……†‡‡‡‡†‡‡‡‡‡‡†††………††††…„ƒƒ„ƒ„ƒƒ‚ƒƒ‚‚‚‚‚‚ƒƒ‚‚ƒ…„‚‚‚€‚‚‚‚ƒ‚‚‚‚‚ƒƒƒƒƒ‚ƒ„ƒƒ„„„„„„„„„…†‡†††††††††………………††‡‡‡†‡ˆ‡††…†‡‡ˆˆˆ‡ˆ‰ŠŠ‰Š‹Š‹‹ŠŠ‰‰Šˆˆˆ‰ˆ‡‡‡‡†…†…†‡†…†‡‡‡‡‡†‡‡‡‡†††……………††…„„„„„„ƒ‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚~€€~~~~~€‚‚‚‚‚‚‚‚‚ƒƒ„„„„„„„ƒ„„„………††††‡……††…„„…†††……†‡‡‡††ˆ†…„…‡‡ˆˆ‰ˆ‡ˆ‰Š‹ŠŠŠŠ‹‰‰‰ŠŠŠŠ‰‰ˆˆ‡‡‡††‡‡††‡‡†…†‡‡‡‡‡‡†‡††…„„„„……†…„„„„…„ƒƒƒ‚‚‚‚‚‚‚€‚…††…‚~~€‚ƒ‚ƒ…„€‚‚‚‚ƒ„ƒ„„„„„……„…………„„……„„…††„„…††††††‡ˆ‡††ˆ†„„†‡‡‡ˆˆ‡‡ˆ‰‰‰‹Š‰Š‹ŠŠ‰Š‹Š‹Š‰‰‰ˆ‡‡†…†‡†‰ˆ‡†…†‡‡‡‡‡††††……„„„„„„ƒ„„„„„„ƒƒƒ‚‚‚‚‚‚‚‚‚€€„…†ˆˆ‡†ƒ„„…†…ƒ€€‚‚‚‚‚ƒ‚‚ƒƒƒ„„„„…………„„„…†††………†††††††‡‡‡‡††‡‡……††‡‡‡††‡‰‰‰‰ŒŒ‹Š‰‰Š‰‰Š‹‹Šˆˆˆˆ†††……†…†‡‡††‡†††………„„„„„„„„„„„„„ƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€€‚„‡‡„€~}}~‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒ„ƒƒƒƒƒƒ„„„„„„………„…………†††…‡‡‡‡ˆ‡†††…†‡‡‡‡†‡‡ˆˆ‡ˆ‰‹‰ˆ‡ˆ‰‰Š‰ˆŠ‹‹Š‰‰ˆ‡†………†‡‡‡‡†††……„ƒ„„„„„„ƒ„„„„„„„…„ƒ‚‚ƒƒƒ‚‚‚‚€€€€€€€€€€€€€€‚ƒ†‰ˆ…ƒ„…‚€€‚‚‚ƒƒƒƒ„„ƒƒƒ„„„„„„„ƒ„†…†…„…††‡…†‡‡‡‡ˆˆ‡……†††‡ˆ‡‡‡ˆˆˆˆ‰ˆ‰‡‡ˆˆˆ‰‰‰‰‰‰ˆ‰ˆ‡‡‡†„ƒ‚…††‡‡…„„„……ƒƒƒƒƒ„„„……„„ƒƒ„„ƒ‚‚‚ƒ‚‚‚‚‚‚€‚‚€€€€€€€‚„†‡‚€€€€€‚‚‚ƒ‚‚ƒƒƒƒ„„„„„„„…††………†††††††ˆŠˆ†…†‡‡‡‡‡‡‡ˆˆˆˆ‰Šˆ‡†‡‰‰‰ŠŠŠ‰‰ŠŠŠ‰‡††……„‚‚…‡ˆ‡…„„„„…„„ƒƒ‚ƒƒƒ„„„ƒƒ„…„ƒ‚‚‚‚‚‚‚€€‚‚€€€€€€€€€€€€€€€€€€‚‚‚‚ƒƒƒƒ„„„……„ƒ„…………††††‡‡†‡ˆˆ†„…†‡‡‡‡‡‡‡‡ˆˆˆˆˆ‡ˆˆ‡‰ŒŠ‰Š‹ŠŠŠŠ‰‡‡ˆ‡†…„‚ƒ†‡‡†…………„„ƒƒ„ƒ‚‚ƒ‚ƒƒ„ƒƒƒƒ„ƒ‚‚‚‚‚‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒƒƒ‚‚ƒƒƒƒƒ„„ƒƒ„„…„„„„…††‡‡…‡ˆ†„„‡‡††‡‡‡ˆˆ‡‡‡ˆ‡†‡ˆˆˆˆ‹‹Š‹Œ‹‰‰‰ˆ‡‡‡ˆ†„ƒ‚‚†ˆ‡†„…………ƒƒ„„ƒƒƒƒ‚‚‚ƒƒ‚‚‚ƒ‚‚‚ƒ‚‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€‚‚‚€‚ƒƒƒƒ„„ƒƒ„…………„„„†……††‡‡…ƒ†‡‡††‡‡‡‡‡‡‡ˆˆ‡‡‡ˆ‰‰‰‰Š‹‹Œ‹‰‰ˆ‡‡‡‡‡‡†„‚ƒ‡‡†„„„„ƒ‚‚ƒ„„ƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚ƒƒ‚|}€}}}€€€€€€€€€€€~~~€€€€€€€€‚ƒƒƒƒƒ„„„„…††…„…†††††‡ˆ„…††††††‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‰‹‹‹‹ŠŠ‰‰ˆ‡‡†‡‡‡‡†††ˆ‡…„ƒƒƒƒƒ‚ƒ„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚~€ƒ‚€€€~|{|€€|{|{{|}|}}~~||}||{{z{|€||}|{||~€€‚‚ƒƒ„„„…„„„…„„„………††‡‡„……………†††††‡ˆˆˆˆ‡‡‡‰ˆˆˆ‰Š‹‰ˆ‰ˆ‡ˆˆ‡‡††††………†‡†……„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€ƒ€~}††}~~}}}|{||}€€€~€€~~€‚}~€}{}€€‚‚‚ƒƒƒƒƒ„ƒƒƒ„„„……………††…………†……†††††††‡‡ˆ‡‡ˆŠ‰‹Šˆ‰‹‰ˆ‰Š‡‡‡‡‡‡††‡†ƒ„……†…„„„„ƒƒ„„ƒ‚‚‚‚‚€€€‚}{~€ƒ‡…}€ƒ‚‚€€~‚„„„„„‚~€„€€€‚……‚€|}€€€€‚‚‚‚‚ƒ„„ƒƒ„„„…„………………†††††††…„„††…††‡ˆˆ‡‡‡‘ŒŠ‰‰‹Š‰Š‰‡‡‡†…†‡††…‚ƒˆ†„„……„„„„ƒƒƒƒƒ€€‚€€}}|~€„…‚€~………‡†‡„€€€}}‚ƒ‚ƒ„ƒ€ƒ€~}‚„‡ˆ‚€{~€€€€€‚‚‚‚ƒƒƒ„„ƒ„„……†††………„………†‡‡†…†††‡‡ˆ‰ˆ‡‡†•‘‹‰‰‰‰‰‰‡‡‡‡†„…‡††„€€€„Ї………„ƒƒƒƒƒ„ƒƒƒ€€€€€€€|~|{€…„€€€€€€€€€€€€€~}~€€€|z~€€‡…€}|€€€€‚‚‚‚‚ƒ„„ƒƒƒƒ„…†††††…†‡‡‡‡‡‡‡†…††††‡ˆ‰ˆˆ‡”’‘ŽŠ‰Š‰‰‰ˆˆ‡‡†„ƒƒ‚€€€€€…Ї…„„„„„„„„„ƒ‚‚‚€€€€€€€€€|}~~~„ƒ€€€€~~~}~~€€€~}|~~~€‚€|y~€€‚„~}€€€‚‚‚‚ƒ„ƒƒ‚ƒƒƒ„…†………†††‡‡‡††ˆ‡‡†††‡ˆ‡‡ˆˆ‡‡“ŽŠˆ‹‰ˆ‰‰‡††††„€€€€€…ˆ†††„‚ƒ„„„ƒƒ‚‚‚‚€€€€€€€€€{|~€~~~€~|}~}|{{{||~~||{{zy{|{~‚€|{~€€€~€€€€€€€‚‚ƒƒ„„„ƒƒ‚ƒƒ„…††††‡‡‡ˆˆ†††††‡‡‡‡‰Šˆ‡ˆˆˆˆ‘Œ‰‹ˆ‡ˆ‡†…†…„~€…ˆ†††…ƒ‚ƒ‚‚‚ƒ‚‚ƒƒ‚€€€€€€€€€€€~}~ƒ}~~~~~~€€€~}}|{~€~~}}}}~z~‚‚||€€€€€€‚ƒ„ƒ‚ƒ‚ƒ„„„„………†‡‡‡‡‡‡†††……‡‡‡‡ˆ‰ˆ‡‡ˆ‰‰‘’’‘ŽŠŠ‰ˆ‰ˆ‡†…‚‚€€€€€ƒˆ‡…„ƒƒƒƒ‚‚‚‚‚ƒƒ‚‚€€€€€€€€~~~ƒ€~}~~~}…†……„ƒ}}€‚‚‚‚‚€€}}||}€‚€€€€€ƒƒ‚„ƒƒƒ„„„………†‡‡ˆˆ‡‡†……††‡‡ˆˆˆ‡‡‡‡ˆ‡Š‘‘’‘ŽŒŒ‰ˆ‡‡†…ƒ€€€€€€ƒˆˆ†……ƒƒ„ƒ‚€€‚‚€€‚€€€€€€€€~}~€‚}~~~~~}€‚‚‚~~ƒ„„„„€€€€‚‚‚‚‚ƒƒƒƒƒ„„…‡†ˆˆˆ‡†„„„…‡‡‰‰ˆˆ‡‡‡‡‡ˆ‹‘‘‰‡…ƒ€€€€€€€‚ƒˆˆ†††„„ƒ‚€€€€€€€€€€€€€€~}}€~~}~~}}~~}|}~~~€‚~‚„„‚€€€€€‚‚‚‚‚‚ƒƒƒƒ…„…†‡‡ˆˆˆ‡†„……†‡‡ˆˆ‡‡‡††ˆ‰ŠŒ’‘‰„€€€€€€€€€€€†ˆ†……„„ƒ‚€‚ƒ‚‚‚‚€‚‚€€€€€€€€}~€€€~~~~||||~~€€~}}~~~~€€€‚~|‚€€€€€€€€€‚‚ƒƒ‚‚‚ƒƒƒƒ…„„…†‡‡‡ˆ‡ˆ†‡‡†…†‡‡ˆ‰‹Š‰ˆ‡††ˆŠŒŒŠ‡ƒ‚€€€€€€€€€„ˆ…ƒ„„ƒƒƒƒ‚‚‚‚ƒƒƒ‚€€€€€~~€~€~~}€~~~}{{{{||}~€~~||}~~~~~ƒ{|€€€€€€€€€‚ƒ„„…ƒ‚ƒ„……„„…„„…††‡‡†‡Šˆ†……„…‡‡‡ˆŠŠ‰ˆˆˆ‡ˆŠŒ‘‘Šˆ†‚€€€€€€€€€€€€„‰„ƒƒƒ‚ƒ‚‚‚€€‚ƒ‚‚€€~€€‚‚~~€€}~~~~~~}}}}|{z}€€~}{{||{zz}‚}|}€€€€€€€‚ƒ„„„„ƒ‚ƒ„„„…„„……††‡‡‡†ˆ‡…„ƒ„†‡‡‡ˆ‰ˆˆˆ‡‡ˆŠ‹‹‰Žˆ†…„€€~€€€€€€€€€„ˆ…„ƒƒ‚‚‚‚‚‚‚€€€~€€€€€‚€€€€€€~~€€~}z}‚~€€€€€€€‚ƒ…„‚‚‚ƒ„„………†‡‡‡‡‡†‡ˆ†…………†‡‡‡ˆˆ‡‡‡ˆˆŠŒŒŠˆ‹‰‡…„………€€€€€€€€€€€€‚‡†„ƒƒƒ‚ƒ‚€€€~}€€€~€€€€€€€‚ƒƒ‚‚‚‚€€€‚‚‚‚~}€ƒ‚€€€€€‚ƒ„„„ƒƒƒƒ„„„………†‡‡‡‡‡‡‡†„„†‡‡‡‡‡‡††††‡‡ˆŠ‰‡‡ˆ……†…„„„ƒ€€€€€€€€€€€€‚ˆ…‚‚‚‚‚‚€‚€€€€~~~~~€€€€€€€€€€€€€€€~~}}~€€ƒ‚‚‚ƒ„………ƒƒ„……„„†‡‡‡‡‡†……„…‡‡ˆˆ††……†‡‡‰Š‹ˆ†‡ƒ…†††…„ƒ€€€€€€€€€€€€‡‡ƒ‚ƒ€€€€€€~~€€€~€€~~~~~~~~~~~~~~~~~}}}}~~~~~~~}~~€€€‚ƒ€€‚ƒƒ„…„……‡†……††‡‡‡‡…†††‡ˆˆˆ†††…†‡ˆ‰‰‰ˆˆˆƒ„……ƒ‚‚€€€€€~€€€€€€…ˆ…‚‚‚‚‚€€€~€€€€€€~}|}~€~~}}~~~~}~~}}~~~~}}}~~~~~~~~€€€€€‚‚‚ƒ‚ƒ…„„…‡‡††††††‡†…††††‡ˆˆ‡……††‡ˆ‹Š‰ˆˆ‡ƒƒ‚‚ƒƒ‚€€€‚€€€€€€€€„ˆ†ƒ‚‚€€€€€€€€€€€€€€€€~|}}||}|}~~}~~~~~~~~}}}~~~~~~}}~~~~}~~€€€€}~‚€€€€€€‚ƒƒƒ…………………†††††ˆ†………„…‡ˆˆ‡††††ˆ‰‹‰ˆ‡†‡…ƒ‚€‚„ƒ€€€€€€€€€€€ƒ‡…‚‚‚€€€€€€€€€€€€€€€€€€€|{}}~~~}{}~~~~~}~}{{||}}}}}|||||}}}}}}}}~}|{{€€€|{z€€€€€€€€€‚ƒƒƒƒ„……„„……†‡††‡ˆ†„…‡„…‡††‡‡††‡‰‹‹ˆˆˆ‡‡ƒƒƒ‚‚‚‚€€€€€€€€€€€€~€†‡‚‚‚€€€€€€€€€€€~|~€€ƒ„‚}}|}~~~~~}|||{|}~}}|}}|{{}}}}}}}}}|z€€‚€|{€€€€€‚ƒ„‚‚„„„„…††‡†‡‡‰…„††††††††………‡ˆ‰‰ˆˆˆˆ‡„ƒƒ€€€€€€€€€€€€„‡ƒ‚‚€€€€€€~{}‚„ƒ€€€~~~~~}|}}{{}}}}}|}}{{}}|}}}|}€}}{€€||€€€‚ƒƒƒ‚ƒƒ„„„…„…†‡‰†‚ƒ…†††‡‡†……‡‡‡‡†‡‡‡ˆˆˆ€€€€€€€€€€€~€€€€€…„„€€€€€€€€€€€€}|~†~~~~}~~~}}}||}~~}|}}}}|{|}}|}}}~~}|}€~~|€€€{}~~~€ƒ‚‚„ƒ„„„„„ƒ„†ˆ‡€ƒƒ‚„…†††……†‡‡ˆˆ†††‡ˆˆˆ€€€€€€€€€€€€€€€€€€€€††…ƒ€€€€€€€€€€€€€€€€€||ƒ‚~}}~~~~~~~}}}|}{{}€~}~}}}}}}~||}~~~}}€~}{~€~~{}€€€€€‚ƒ„„„„„„…ˆ‹‚~€‚‚ƒ†‡‡††…†‡‡‡ˆ†‡‡‡‡‡‡€€€€€€€€€€€€€€€€€€€„‡„‚€€€€€€€€€||‚€~~}~~~~}}}}||||}}~€~~}|}}}~~}~~{||}~~}}~~}}~~‚|}€€€€€€€€€€€€‚„ƒƒƒ‚‚„‰‡‚ƒ‡†……………‡‡‡‡‡ˆˆ‡‡‡‡€€‚€€€€€€€€€€€€€€€€€€‚†ƒ€€€€€€€€€~€€~~~€~~~~~~}}}}}}|{|}€~}}~}}}}|}|}|{|~~€~~~~~~ƒ€||€€€€€€€€€€€€€‚‚€‚ƒ…Š‚€€‚……ƒƒ„„†††‡‡‡‡‡‡ˆˆˆˆ€€€€€€€€€€€€€€€€€€€€„„ƒ‚€€€€€€€€}€~~~~~~~}}}~}|||€~~}}}|}|||}{||~{{{}}~}€€~~~}{}z|€€€€€€€€€€€€ƒ‰†€€ƒƒ‚ƒ„††††‡‡ˆ‰ˆ‡‡‡‡‰€€€€€‚€€€€€€€€€€€€„†„‚€€€€€€€~~~~~~|~~~}}~}}}}}}||~€}||||||}}}|~~}{|||}~~~}{}‚~{|€€|y}~€€€€€€€€€€€€€ƒƒƒ‡‰€€€ƒƒ„…†‡……„…‡‡‡‡‡†‡‡‡€€€€€€€€€€€€€€~€€€€„„ƒ‚€€€€€~~~}~~~}}~}~~||}|{}}}|}~~~}{||}~|z{{|}|}}|||||}~€}|||€‚~|z~‚~z}~~€€€€€€€€€€€‚‚…Šƒ€€€€€€€ƒ„…††…„„„‡‡‡‡‡ˆˆ‰ˆ€€€€€€€€€€€€€€€€€„ƒƒƒ€€€€€~~~~~~}}~~}{z{|||}z{}~}}~~}|{z|{||||~~{{|~~}{}~‚z{‚{|~€€€€€€€€€€€‚‰‡€€€€~~ƒ„…†††…†‡‡‡‡‡‡‡‡ˆ€€‚€€€€€€€€€€€€€€€€†„„„‚€€€€€€€~~}~}}}~~~€||}~~~~}|}}||~}~~}|||||}y|}{|~€ƒ{|€€€€€€€€€€ƒ†‰‚€€€€€€€€ƒ…ƒ„…†‡††††‡ˆ‡‡ˆ‰€€€€‚€€€€€€€€€€€€€€€€„ƒ‚€€€€€€€€~~~~~}~€€€}|}~}||~~}}}}}|||~~~~|||}{{€~{x{}}~~~‚~{|€€€€€€€€€€€ƒ‰„~€€€€€€„„ƒ„…†…†‡……‡ˆ‡‡ˆ€€€€€€€€‚€€€€€€€€…€‚„€€€€~~~~~€€€}}~~~~}||{|||{|~}{}}|}}|}}}}{z~{y{|}}}~}~~zz~€€~~€€€€€‚…‰€€€€€€€‚ƒ„„…†‡‡…†ˆ‰Š‰Š€€€€€€€€€€~~€€€€€€€†€ƒ„‚€€€€€€~~~~~~~}}}}}}}~~~~||}}}}|{{}||||{|~~}~~}}}}}}}}zz~}||||}}}}}€||~€€€€‚…‡‚€€€€€€‚„„…†‡ˆ‡ˆ‰‰ŠŠ‹|~€€€~€€€€€€€€€€€‚ˆ~„„‚€€€€€~~~}~~}}}}}}}}}}}}}}|{|{z|{z|}~~}}}~~~}}~~||}}}}||}}~~~~~€€€€‚„„‡‚€€€€€€€€~ƒƒ„…††‡‡ˆŠŽŽqrruy{~€€€€€€€€€€‚‰ƒ€ƒ„ƒ‚€€€€€~~~~~~~}}}}||}~}}}~}}}}{{z{{{{||||||||}}|||||||||||}}}~~~~~~~~~~~~€€€€„…ƒ‡€€€€€€€€€€€~‚„„†‡‡‡‰ŒŒŽŽŽmkkmnnv€€€€€€€€€€€€ƒˆ…‚‚‚€€€€€€~~~~~~~~~~~~}}}}}}}||||}}}||{{||||}|||||{{{{{{|}}}}||}}}}}}}}~~~~~~~}~€€€€€€€€‚…‚‚ˆ€€€€€€€€ƒ†‡‡‰‰‰Œ‘kjjkkjr€€€€~€€€€€€€€€€€€‚†ƒ‚‚‚‚€€€€€~~~~~~~~}}|}}|}}}|}{{|}}||||{{{{|||||}}|||}|||}}|||}}~~~~~}}~~}~€€€€€€€„‚€ƒˆ‚€€€€€€€€€€€€€„†ˆˆˆŠŒ‘’kjhhigr€€€€€€€€€€€€€ƒ†ƒ‚‚‚‚ƒ€€€€€~~~~~~~~~}~}}}|}}}|{||{|}}||||{{{{{|||||}}||}||||||||{|}~}}}}}~~~€€€€€€€„„‚‰‚€€€€€€€€€€€€€€€€€ƒ…‡‡ˆ‹‘hhhhhdp~€€€€€€€†‡€€‚€€€€€~~}~~~}}}|}|||||||}||}}{z|||||{||}|{||{{{||{||}}}|{|||}}}~~~~~€€€€‚„‚ƒ„‰‚€€€€€€€€€€€€€€ƒ„†ˆ‰ŒŒfffefbp€€€€€€€€€€€€€†ˆ€ƒ€€€€~~~~~~~}}}}}}||||||||||{|~}}{z~}}{z~}{{{{z|}}~}}|}}}}}}~~~~~~~~€€€€€€‚ƒƒ‚„‰‚€€€€€€€‚„†ˆ‰‹ŒŒŽffedd`p~€~€€€€€€€‡‰€€€‚‚‚€€€€€~~~~}~~~~}}}~}}|||{{||{{|||}}~~~}~}z}{z|}~}z|}|||||}}}}|}}}~~~€€€€ƒ„„ƒƒ‡ƒ€€€€€€€€€€~€€‚†‡‡ˆ‰‹ŒŽeeeecbr€€€~~€€€€€€€€€€ˆˆ€€€‚€€€€€€~~~}~~}||}}}}|||}}z|~}~~|}}~}~}{||y|||~xz|{||}}}}~~}~~~~~€€€€€€€€€‚ƒ‚‚‚‚‚†„€€€€€€€€€€€€ƒ††‡ˆ‰‹ŽŽccccb`q~~€€€€€~€€€ˆ‡€€€€€€€€€€€~~~~||}}}}|||~|{}}}}}|{{~|{{{{|y{|}~x|}}||}}}}~~~~€€€€~€‚‚‚ƒ‡„€€€€€€€€€€‚„„‡‡ˆŠŽŽŽedbaa_p€€€€€€€€€€€€€€€ˆ‡€€€€€€€€‚€€~~~}}~~~~~~}}}}}|{{{{||}}|z{}}|}}}}}}{{|{|}|zz~~}|{}~}}}~~~~~~}}~€‚‚€ƒ‡†€€€€€€€€€~……†‡‡ŠŒŽŽdddcb_q€€€€€€€€€€€€€€~€€Šˆ€€€€€ƒ‚€€€~||}}~~}}}}||{|}}||{|~}{{}}|||{|}|{{}|{{z{~~{{|||||||}}}~~~~€~~~ƒ‚€€‚ƒ‰‡€€€~~~~€€€…†…‡‡Š‹ŒŽcccca`p€€€€€€€€€€€‰‡€€€€€€€€€€€€€€€}}}}}|}}|}}|{|}}||}}}}|||}|}}{{{|~}}}~~~~}{|}}}}||||}}~~~~~‚„ƒ€‚‚ˆ‡~~~ƒ†‡‡‡ˆŠ‹Œcbbccan€€€~~€€€€Š‡€€€€€€€€€€€€€€€€~}}}||||}}|{{{{||{{}~}|||{{}}}}}}||}~~}}|z{}}|||{{|}}~~~~€ƒ‚‚€€€‡‡€€~~‚†‡‡‡‰‹Ždddcbal€€~€€€‚€€€‚Š…€€€€€€€€€€€~~}}~~}}}||||{{{{||{{{||{{{zz||||{{{|||{{|{{{{{|}}}}}}~~€~~~~€‚‚€€€€‡‡€€€€€€€€~ƒ†‡‡ˆ‰‹ŒŽcccbbaj~€€€€€€€€€~‹„€€€€€€€~~~}~~~}|}}||||}}|||||||{{{{{|{{zzzz{{|{{{{{{{{|}}}~~~~~~~~~~~€€€€€€€€‡‡€€€€€€€€€€€~€„†‡†ˆ‰‹Œdcccdcj~€€€€€€€€€€€Š…€€€€€€€€€€€~~~}}||{|}}}}}|}~~~~~}}}}|||{z{{{{}}}~}}}}||||}}}~~~~~}}~~~€‚€€€€€‚†‡€€€€€€ƒ‡‡†‰ˆˆŒ“ddeeecg|€~€€€€€€€€€€€ˆ„€€€€€€€€€€€€€€~~~~}}||||}}}}{{||{{|z{}~}|}|{|}~~}zz||z{|~~}}}}}~~}~~~~}~~ƒ„€€€€€ƒ€€‡†€€€€€€€€€€†††‰‰ŠŒ’eeeffdf{€€€€€€€€€€€€€€‰„€€€€€€€€€€€€~~~~}}|||}}|}|zzzzz{{}~}}|{|~}z}}|||{{z{|~~~|}~~~~~~|||}~€€‚„‚€€€ƒ€‡‡€€€€€€€€€€ƒƒ‚…†‡‡ˆ‹Ž‘fghfhfgz€€€€€€€€€€€€€€Š…€€€€€€€€€€€€€€€€~~~~~}}}||||||}}|{zzz{{{|~}|{~}z{}{zz{{{|}z|~}}}}}}~~}|}~~€ƒƒ€€€~‚‡‡~€€€€€€€ƒ„‚ƒ„…†ˆ‹efffgegx€€€€€€€€€€~‚‹…‚‚€€€€€€€€€€€€€~~~~}~}|}}}{{||~~}}}{|~|}}||}{||z{}~~}}|~}z|}}}~~~~}}}}~€€€€€€€€ƒˆ†€€€€€€€€€€€€„…„†‡‡ŠŽeffhjjiv~€€€€€€€€€€‚‹„‚€€‚€€€€€€€€€€€~~~}}}}|}}}{{||~}|||||}|||||||{{{|}}||}~}}{|~~~~~~~~~}}~€€‚ƒ€€€€~€ƒ€€ƒŠ„~€€€€€€€~€€‚„……†ˆˆŠijkjkkks~€€€€~€€€‰…‚€€€€€€€€€€€€€€€€~~~~}}}}{{}}~|{{||}|}{{|}~z||{{|}}}~~}|{}|}}~~~~€‚€€~‚ƒ€‰ƒ~€‚€ƒ‚„†ˆ‰‹jjjjloms}€~€€€€€€€€€€‰…‚‚€€€€€~€€€€€€€~~}}}~|y}~~|||||}|}|{~||||}}}|||}}~~}|{|{|}}}~~€€€‚€€€€€‚ƒ‰ƒ€€€€€€€€€€€€€€‚„…‡‡Šmlmklmnu~€€€€€€€€€€€Š„‚€€€€€€€€€€€€€€€€€€~}}~~~~|{}|}}}}}|}}~|{}z{{|}|||||||}€~|z}~}}}}}~€€€€€€€€„€‚Šƒ€€€€€€€€€€€€€€…†‡‡ˆnmoonoqu}€€€€€€€€€€€‚‰…‚€€€€€€€€€€€€€€€€€€}}~~|}~}}}}}}}}}~|{}{|{{|{{||||}~€|zz}}}}}}~~€€€€€€€€€€€€„€€„Œƒ€€€€€€~€€‚‚ƒ„ƒ…†noopqpps}€€€€€€€€€€€€€…‡„‚€€€€€€~€€€€€€~~~~{|}~}}}|}||{|~{{||}||||}}~~|{z}}}}~~€€€€€€€€€€€€‚„€„‹‚€€€‚‚ƒƒ‚ƒnpnnqrrs|€€€€€€€€€€€€€€ˆ„‚‚€€€€€€€€€€€€~~~~}||}~}}}~}|||~}{z||}}~}}}€||z|}|}~~€€€€€€€€€€~€€„ƒ€„‰€€€€€€~€€€‚ƒ…oonprstu{€€€€€€€€…Šƒƒƒ€€€€€€€€€€~}~}}}€€~|}|}~}|z{{}~}}‚€|zy|}}~~~€€€€€€€~‚…‚€€…ˆ‚€€€€€€€€~€€€‚…qrqrssuvz€€€€€€€€€€€†ˆƒ‚€€€€€€€€~€~~~}~}}~~~|}€~~}|}}|{~~~}{z||~€€€€~|y|}}}~~~€‚€€€‚„€€€…‡…€€€€€€€€€€€€ƒ‚‚‚rsrsuvvwy€€€€€€€€€€‚‰…€ƒ‚‚€€€‚‚€€~}€~~~{}~|z{|}}|{|}}}}|{|~}||||||{x{}|~~~~€€€‚€~€€„ƒ€„‡‰‚€€€€€€€€€‚ƒ‚‚‚rrsuwuvwy€€€€€„Š„ƒƒƒ‚€€‚€€€€€€€~~€€~~€€~}}{{{{{{zz{z|~}||}}}~|{|}}}}|zx{~}|~~~€€€€~}}€‚„„…ˆƒ€€€€€€€€€€€€ƒ„‚‚ƒ‚‚stuvxwwwy~€€€€€€€€‰‹ƒ‚„„ƒ‚‚‚€€€€€€€€€€€~~}}~~~~~~~||{||{{{}~}}}}~}}}~~}|{yyxxy}~~~~~€€‚€€€€€~~€€ƒƒ‚‚‚„„††€€€€€€€~€€€‚€rtuwxxxxy}€~€€€€€€€€‚‹ˆ‚‚„„ƒƒ‚‚‚€€€‚€€€€€€€‚~~||}}}}~~~~~€~~}}~}}}}~~~|}}}}}}~€€€€€„ƒ€€€€~€€…ƒ‚ƒ„„…‰€€€€€€€€€‚‚„ƒ€tuwwxxyz{|€€€€€€€€€€€€„‹…ƒ„„„ƒƒ‚€€€‚€€€€€ƒ€€}}|}}}}~~~~~~~}~~~~~}}~~~~}~~~}~~}}}~€€€€ƒ‚€‚‚€€€€€€€€€‚…€ƒ„ƒ„‰„€€€€€€ƒƒ‚ƒƒƒ‚€wwxyyyz{{}€€€€€€€€€€€€‡‹†„„„„ƒ‚‚€‚‚€€€€~~€€€€€€~}||}}}}}~~}}}}~~}}~~}~}}}}|||||~~€€‚„‚~€€€€€€‚„€€€ƒ„ƒ„††€€€€€€€€€€€‚ƒ‚‚‚‚ƒ‚€‚‚‚wxxyy{~€€€€€€€€€€€‹‹†„‚ƒ…„ƒ‚‚‚‚€€€‚€€~~‚ƒ€€€}~€~}}}|||}}|}~~}}~~~~}}}|}}}||}}~~€€‚„‚‚„~~~}z~€€€€‚„‚€€‚„ƒƒ…‰€€€€€€€€€€€‚„‚‚‚‚ƒxz{|~~€€€€€€€€€€ƒŒ‰…„‚„†…„‚‚ƒ‚€€‚€€}}„ƒ€€}}}€€~~}|~}}}}}}}}}}|||}€‚ƒƒ‚€€‚|…„|}}|x}€€€€€ƒ†ƒ€‚ƒ…ƒ‚……Š„€€€€€€€€€€€€‚‚ƒƒƒ‚ƒ„ƒƒ{~€€€€€€€€€€€€€€…Œ‡……ƒƒ„„ƒƒ‚‚‚‚€}}€„„€€~}~€~||~~~~~~}‚€~}}~~~€‚ƒ~|ƒ†€|~€}|€€€„„‚€€‚‚‚…ƒ‚„…‰…€€€€€€€€€€ƒƒ‚‚‚‚‚‚„ƒƒ€€€€€ƒ‚€€€€€‚ˆ‹††…ƒ‚ƒƒƒƒƒƒ‚€‚‚‚~}…‚}|~€€~~€~~~€€€~€ƒ€|zyxxx{~~|€ƒ‚|}…‚~~€€€€€€€‚…ƒ€€‚‚ƒ…‚ƒ……‡ˆ€€€€€€‚€€ƒƒƒƒƒƒ‚ƒ„ƒƒ„ƒ€€€€€€€€‚‚Š‹††…„‚„„„ƒƒƒƒ€€€€€~~€~ƒ…€|}€€€~€€€~ƒ€|xwy|~}}{{z|€ƒ€|€‚}~€~€€€‚„„‚€‚‚ƒ„…‚ƒ……†‰„€€€€€€€…ƒ‚„„ƒƒƒƒ„„‚„„€‚‚‚‚‚‚€€€€‚€€€€‚‹‰†††„‚ƒ„„„ƒƒƒƒ‚€€€€€€€€~‚}†}~€€€€€}|{}}€~ƒ€}yy~€€€€{{|{|}€€€€€€€‚‚ƒ…‚€€‚ƒƒ„„‚ƒ„…†‡…€€€€€ƒ„†„‚„„ƒƒƒ„„ƒ‚‚ƒ‚„……‚€€€€€€€€€€…Œ‡‡‡†„‚ƒ„„„ƒƒƒ‚‚€€€€€€~‚|‚€}|~‚‚€€€||{}}~€€€€‚|zz€€€€€€€‚{|€~{|€€€€€€€€€€‚‚„„€‚‚‚ƒ„ƒƒ„„…†‡ˆ€€€€€ƒ€€‚„††‚‚„ƒ‚ƒ‚‚ƒ„ƒ„„ƒ„‚€€€€€€ˆŒ†††…„‚ƒ†……„„„„‚€‚€€€€‚‚}}~||‚‚€€€‚||~~}€€€€€€|}{€€€€ƒ|}‚~{~€€€€ƒ„ƒ‚€‚‚‚ƒ…„ƒ„„…„†Šƒ€€€€‚ƒ€„…†„ƒ…ƒ‚ƒ‚‚‚‚ƒƒ„…………„ƒ€€€€€€€€€Š‹†………„„…††…„„„„ƒ‚€€€}€€~|}‚‚€€€€}}}€|~~‚€€~}}€€€€‚„„ƒƒ|~ƒ|€€€€‚‚€€‚…„€‚…†„ƒ„„„…†‰‡€€‚‚‚„†…„‚„…ƒ‚‚‚ƒ„‚……„ƒ‚„†„„€~€€‚„‹Š†††……ƒƒ…†…„„„…„ƒ‚‚‚‚‚‚~€}ƒ‚€€~}~‚‚}|}ƒ‚~|‚€€€€€€€~{~€|{‚}€€‡ƒ‚‚ƒ…‚€€€‚ƒ‚‚…†„‚ƒ„………‡ˆ‚€€‚ƒ„„ƒ……„…„ƒƒƒƒ‚‚ƒƒ„‚„„…†……„‚‚€€€€€€€„Љ‡‡‡‡†„„„„„„„„„ƒ‚‚‚‚~~ƒ|€|„‚€€~~…„„…|}€‚‚€~~€‚€€€{|~}{€}z~}~„„„‡†y|ƒ„ƒ‚‚ƒ„ƒƒ…†ƒƒ„„……†‡ˆ†€€„…„ƒƒƒƒƒ…‡ƒƒ‚‚‚‚ƒƒ…„……„…††…„„ƒ~€€€€‡‰‰‡‡‡‡†ƒ„………„„„„„‚€‚‚€|z‚€}€{~~~~~€€~~‚‚‚€~~€~~€~xx|}}}€ƒ~z}{{ƒ„ƒ€}y~€„…ƒ‚ƒ‚‚ƒ…‡…ƒƒ„„…†‡‡ˆ‡€€ƒ„ƒ‚ƒƒ…„‚‚‚ƒ†††††‡‡‡†…‡†€€€€€‚ˆˆ‡‡‡‡††„„††…„ƒ„„ƒƒ€‚€€~z~€€~|~€€~}}|{{}}}}€€€}}~€ƒ€~~~€„€‚ƒ~|z{}~~|z€‚„„ƒ€‚‚‚„††……„„„…†…†ˆˆ„€‚„ƒ‚‚„ƒ‚‚„„‚‚ƒ‚‚„††………†††‡†††€€€€€€†ˆ‡ˆ‡‡‡‡‡†„„„………„„„ƒƒ‚‚€€€€€|~‚ƒ‚€€~}~|{{zzz}~€€~|}~„ƒ€……~|}~€€|{{{yz„…„‚‚‚‚ƒ„††…„„„„„………†‰‰€€‚ƒ‚‚ƒ„…ƒ…†ƒ„ƒ„……ƒƒ†††…„†††††‡†€€„Œ‡‡‡‡‡‡†††„ƒ„…„„„„„ƒƒ‚‚€€‚€z{€ƒ|~~€€~€ƒ|z}‚ƒƒ„‚€~{}‚‚~}‚‚‚~{{|€€ƒ…„‚€‚ƒƒƒ„„†ˆ…„ƒƒ…†‡‡…†‰Šƒƒ‚„„………‡……††††„‚‡†ˆˆ‡‡‰ˆ††‡‡ƒ~€€€‡Š‡†‡‡‡††††„‚…††…………ƒƒ‚‚€‚‚€‚€}}€}yy{€€€€€|{|~}{}~~€€{{€‚ƒzz‚‚‚€€‚ƒ„„ƒ‚‚ƒƒƒ‚ƒ„…‡†„„………‡‡‡††ˆ‰‚€€‚ƒƒ„………………‡ˆ†ƒ‚†‡‰‰ˆ‡‡‡‡‡‡ˆ†€€€‚‰‡‡‡‡ˆ‡‡‡‡‡…ƒ…‡†††††…„ƒ‚‚‚ƒ„ƒƒ‚‚‚€€€€€€€€€€€€€|zz{}€€€~{||}}~}{|€|}€€€€‚‚ƒ……„ƒ‚ƒƒ„ƒƒ…††…ƒƒ„††‡‡‡‡‡‡‰‡…„„„ƒ‚ƒ…………„ƒ……ƒ‚ƒ‡…†‡†„„†‡‡†‡‡ƒ€€‚ƒ†Š‡ˆ‡‡‡††‡‡‡…‚„†††††††……„ƒƒƒ„„ƒ‚‚€€€€€€€€€€€€€€€€€€€~|yyz{~‚€‚‚€€€‚‚„†…„ƒ‚‚„„„„…†‡†…„„„„†‡‡‡‡‡ˆˆˆ†„‚ƒ……ƒ‚€ƒ…††…ƒ„†‚ƒˆ‡†‡ˆ‡‡ˆˆˆ‡‡ˆ„€ƒŠ‰‡‡‡ˆˆˆˆˆ‡‡†„ƒ…†‡…………………„‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€‚‚€€€€€€€€€€€€€€€€€€€‚ƒ‚‚‚‚€‚ƒ‚‚„„„ƒƒ‚‚ƒ„„„ƒ…†ˆ†ƒ„…………††‡‡†‡ˆˆ‡ƒ‚‚†…ƒƒ‚ƒ‡‡…†„ƒ…‚€‡‡‡‡‡‡ˆˆˆ‰ˆˆˆƒ‚‡Œˆ‡ˆ‡‡‰Š‰‡‡ˆ†„„…†††……†………„‚ƒƒ‚ƒƒƒ‚‚‚‚‚ƒƒ‚‚‚‚‚‚€~€€‚‚‚‚‚€‚‚‚‚‚‚‚‚‚ƒƒƒƒ„……„ƒ‚‚ƒ„„„…†‡‡…„…†…†‡‡‡ˆˆ‡‡‡ˆˆƒ‚†…ƒ‚ƒƒƒ„……ƒƒƒ‚ƒƒˆ‡ˆ‡†‡‡‰ˆ‡††…„€‚‹‰‡‰‡‡‰‰‰‡‡‰‡„ƒ…‡‡†††…†††…„„ƒƒƒ‚ƒƒ‚‚‚‚‚‚€‚‚€€€€€€‚‚‚‚‚‚‚‚ƒƒƒƒƒ‚‚‚ƒ‚‚‚‚‚‚‚ƒ„…„„„ƒ‚‚ƒ‚ƒƒ…††‡†„„„…††‡††††††‡ˆ‰†„ƒƒ…ƒ‚„„„…†„„ƒƒ‡…‰‡‡‡†††ˆˆ‡‡‡‡†€~‡Š‡‡ˆ‡‰‹Šˆˆˆ‰ˆ…ƒ…††‡††††††……ƒƒ‚‚‚ƒƒ‚‚‚ƒƒ‚‚€€€€€€€€€‚‚‚ƒ‚‚ƒƒ‚‚‚„„ƒ„ƒƒ‚‚‚‚€€‚ƒƒƒ„††„„„ƒƒ„ƒƒ„…††‡…„…††††††…††††‡ˆŠ‡„†††…ƒƒ„†…„…………„…„Šˆ‡††‡‡‡……‡‡‡‡…‚ŠŒŠ‡ˆ‰ˆˆŠ‰‡‡‰‰ˆ†„„……‡†††‡††††„ƒ„„ƒƒ‚‚ƒƒƒƒ‚‚‚‚‚€€€ƒƒ‚‚‚ƒ‚ƒƒ‚‚‚‚ƒƒ‚€€€ƒ„…„††„ƒ„„‚‚„„„„…††‡‡„„…„…††‡ˆ‡†††‡‡‡‰‰ƒ‡†…„„††††…………………
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg Binary files differnew file mode 100644 index 0000000000..20b5a2c0df --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg Binary files differnew file mode 100644 index 0000000000..41300f47f1 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.y b/libs/jpegrecoverymap/tests/data/minnie-320x240.y new file mode 100644 index 0000000000..f9d8371c18 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.y @@ -0,0 +1,1930 @@ +ØÖÖÓÑÑÏËÈÈÈÅÅÃÃÂÅÂÁÀÁÀ½¹¶³°«¬±´³²±´¯–‘ž°¯¯±´³³´µ¹»½½½»¸¸·¹»¼½¿ÁÃÆÇÉÌÎÏÐÎÍÌËÊÊÉÊÍÌËÊÆÇÈËÊÌÌÉÈÇÆÅÄÄÅÆÇÆÆÇÇÆÅÃÄÂÃÁÁÁÁÁ¿¾¼¼¼¼½»»¸·¹¸·¶µ³²²²²²±°¯¯®®®ª©¦£¤¥¤“}vrswqnpsqqklspmjmnlknw{’™›œ›š›››™›ššœœšœžŸ¡¢¤§«¯³µ¸ºº¼¼»º¹µ²¥ž—”’”¦®´µ·¸¹º»»¼»¼¼½¾¾¾½¼º¹¸¹º»¼»½ÀÂÃÆÊËÊÊÍÍÌÌÉžºµ±©©´¶¶¶³²²³°¬©²³¯©¢¡¨±´·µµµ³›…„•¬¸º¹··³¨ ¦¯±£W9ÙÙ×ÕÒÑÏÎÉÇÇÈÅ¿¿ÁÄÂÀÀļ·´±¬«®±²°°§Ž›®¯¯°²²²µ¹»½¼¼¼º¸¸¹»½¾¾ÁÅÇÆÉÍÏÐÏÎÎÌËÌÌËËËËÌÊÇÇÉÊËÌÌÌÊÉÈÈÆÇÇÇÈÇÇÆÆÆÆÆÆÄÃÃÃÂÂÁ¿½¼¼½½¾»¹¹¹¹¹·¶¶µ´µ´²±²±¯¯®¨¤¢¥¤f96999:7464336753/01,..2344N“žœœššœœœœžž¡£¦«®°³µ¸»¼½½¼¼¹´®¨¡›•”–𤫳¶¶·¸º¼½½¾¼½½¾¾¾¿¿½»¹¹ººº»¼¾ÀÂÅÈËÌËÎÏÐÎÊÆÄ½¸µ±¬¨§¯·¶µ´²±²²°©®²³®§¥¥«°³¶¶µ´¯¨“‚ˆ¡³¶¸···³¨¡§°´²w:ÚÚØÖÓÒÐÐÍÊÇÇŽ¾½¿ÁÁÁÃÄÁ»µ²¯®ª«°°¬¥£¬“¨®¯°±°±µº½¼¼¼¼»¹¸¹º¼½¿ÁÅÈÉÌÎÐÐÎÍÍÊÊÌÍÍÌÌÌËÉÈÈÈÊËÌËËËÊÊÉÈÈÈÇÆÅÆÆÆÆÆÇÆÆÄÄÃÂÂÁ¿½¼½½½¾¼¹ºº¹¸·¶µµ´´µ´²±±¯¯¬¬§¤ªr'Rouwwvzy{zxyzyxtsvurkgce_F U¢žœ›››œœ››Ÿ ¡£¨«°±³µ¸»½¿¾¾½º·°«£›–••˜ ¨®´¶¶¹»¼½½¾¾¾¾¾¾¾¾¿¿¼»º¹¹º»¼½¿ÁÄÇÊÍÍÍÑÒÏËÉÄÁ»·´±§¨²·¶´³³³²²¯«©®²³¨¤¦°³··¶µ³®£‰–®µµ¶¸¸¸²¤ §±¶·OÙ×ÙØÔÑÐÍÎËÈÅÄÃÀ¿¼º¼½ÀÁÂĺµ²¯®®©™”ž£–£ª¯°°±°²¹½½¼»¼º¹¸¹¹»¼¿ÁÄÈÊÌÎÑÑÏÍÌÊÊËÌÌÌËÌÊÉÉÈÈÊËËÊÊËÊÊÈÉÉÈÇÆÆÆÅÆÅÄÆÃÄÄÁÁÂÂÁ¿½»¼½¾½»¼»º¹·¶¶··´´µµ´²±°°®¯®«ª¥¤@: ¬ª¬©ª¨§§©ª§©¨§©¦£¦¦£•¥£¤•;:—Ÿžœ™š››œ›œœŸ£¥§«®±´·»½¾¿À¿½»¸³¬§Ÿ™•”–ž¦°³¶¹»½¾½¾½¾¾¾¿¿¿¿¿½¼»º»º»¼¼½ÁÂÇÊÊÌÏÑÑÐÏÌȾ¹´¯¯¬§«´¶¶´µ¶³²±¯ª¨³²«¨¢©¶¹ºº·¶²¬™~‹¢°±²µ·¹¶®£ ¦±··¯oÛ×Ö×ÕÒÐÌÍÊÇÇÅÁ¿¾º¸·¹¼¿Á¿¹µ²¯®®°«—‰‘œš¡©ª®²²°³º¼¼¼»»º¹¹¸¸¹¼¿ÀÄÇÈÌÎÐÑÏÎÍËÉÊËËÍÍËËÊÉÈÈËËÊÊËËÊÉÊËËÉÇÆÈÇÇÆÄÄÅÄÄÄÃÃÂÂÁ¿½½½¾¾½»º»»¹¹¸¸¹º·µ¸·¶¶³²³°°°°®ª 5M¦¡¢¡¢¤¤¦¥£££¤¤¢ ¡ £}>Ž¡ž¥I1‘Ÿœš™™›ššš›œ›œ £¦«¯²´·º»¾¿¿¿¾½»µ®©£›˜–•›£©¯³¶·¹½¾¿¿¿¿½½¾¾¿¿À¿¾½»º¼»¼¼½¿ÁÃÇÊÌÎÏÑÑÐÎÊÆÁ¼¶±«ª¨¨³µµ´´´´³²¯©§±®«¦£´¸º¹¶´°¤‰€’§«°³¶·²£ §±¸¹²•ÛÙ×ÓÑÓÏËÊÈÄÄÃÀÁ¿¼º¶´µ¸½ÀÃÿ¹´²°¬«£ˆˆ”›Ÿ§ª«¬°±¯°º½½½½»»º¹·¸º¼ÀÀÃÆÈËÎÐÐÐÏÍÌÊËÌÌÍÌËËÊÉÇÈÊËÊÊÉÊÊËËÉÈÈÈÉÈÇÆÇÇÆÄÃÄÃÅÄÃÂÂÀ¾¾ÀÀÀ¿½»»¼¼»ºº¹¹¹¸¸¸¸¸µµµ³³³²°¯¬5T§¡£¢¤¥¦¦¦¦¥¥¥¦¤¢¢¡ ¢T1s¥Ÿ¢F)sš›—–————˜™››ž £¦ª¯³¶¸º½¿¿¿¾¾½»¹±¦Ÿ™•–›£¨®³µ·¹»½¾À¿¾¾¾½½¾¿ÀÀ¾¾¼ºº»»½¾¿ÀÂÄÈÊÍÏÏÐÑÏÌÈÄ¿º´©§¦§°µ´´³´³³³²¯¨©±±¨¢¥°¶¸ºº·²¬ƒ‰˜¤¨¬¯²µ¶²¤¤§²»º²¬ÚØØÕÐÏÎÌËÉÃÁÀ¿¿À¾º·²°²¶»¿Â¼¸´²®¨©ª„Œ”ž©©««®°±°¸»¼½½»»»¹¹¸»½¿ÂÄÆÆÊÏÑÑÑÐÍËÍÎÎÎÌËÊÈÊÊÇÆÉÊÌÌËËÊÊÉÈÇÈÈÇÇÇÇÈÉÈÅÄÄÄÆÄÃÄÂÁÀ¿¿¿ÀÀ¾¾½½½¼»¼»¹¸¹º¹··¶µ´´µ´²²°™2Z©¤¦¥¦¥¦¥¦¥¥¤¥¤¤¤£¢£‡HeP¡ ¡@&-Hh‰—–’”—ššœ ¤¦©°µº»½¿¿ÀÀ¿¿¾»¶®©¡œ˜•™¢ª®²¶·ºº¼¾¾¾½½½½»½¿¿À¿¿¾»¼»º¼½¿¿ÂÄÆÊËÎÏÐÒÒÎËÇ»·°«¨¦¥¦²³´´´´µ´²¦©¯¯¥ ¥·¹»»º·°§Š™¥©¬°²µµ´«¡¤©´½»³¯ØÕÖÕÑÌÊÊÉÇÄÁ½»¾¿¿»¸³¯¯´¹½Á¿»¶²©§£…ˆŽ—¥§©«¬®®·¼¾»¹»¼»ººº»½¿ÂÄÆÇÍÑÑÓÒÏÍÌÌËÌÍÌËËËËÊÈÈÉÉËÌÍÌÊÊÉÈÈÇÆÄÅÅÆÉÉÇÅÄÄÆÅÂÂÃÃÁÀ¿¾¾ÀÀ¿¾½¾¾½»»º¹¸ºº¹¸¸·¶¶¶µµ´³±°–0b¥¦¦§¥¥¦¦¥¥¤¥¤¤£££¨_gA‘¡š8$A,&6W†—›š›ž ¤§ª®²µ¹»½¿¿ÀÀÀ¾½¼·±©£ž™—™ž¦«¯²¶¸¼½¿¿½»»¼½»»½¿¿¿¿½½»»ººº»½ÀÃÆÉÌÍÐÑÑÒÒÏËÆÀ¹³©¨¥¤¨¬°²³³´··¶±«¤¨©¢ž«¸½»¹·³® „ƒŸ¨«®¯°µ·²§Ÿ£«´»½µ®ÐÌÎÐÒÎÊÇÆÆÅÁ¾¼¼¾¾½¹´°«¬®²·½¿½¶³®ª§¬°‰Œ” ¤¦¨ª©©ª¶¼¼º¹¹¹º»º»½¾ÁÂÅÈÊÍÐÐÒÑÎÌËËÌÌÍÍÌËËÊÈÇÆÆÈËËÊËÊÊÊÉÉÈÆÄÄÅÅÅÆÆÅÆÄÅÅÄÄÄÿ½½½¾¿ÀÁÀ¿½½½»»º¹·¹¹¹»º¹¹¸¶µ´³´³³•/l²§¦¦§§¨¨¨¨§¦¦¥¥¥¤¤—?S_8z¤”06‘}V5$.YšŸž¡¦ª¬®²µ¸¼¼¿ÁÁÂÁÀ¿½·³§¢™™Ÿ¥¬°±´¸»½ÀÁÀ¾½¾¿¿¼½¿À¿¾¾¿½º¹¹¹¹»¾ÂÄÈÌÍÑÓÓÓÓÐÍÇž·±¬¨¦¤¥ª°²²²³µ··´¯¨£§««¤Ÿ¡±¹½»·µ±ª“‰•¢ª¬¬²¶¶¯§ ¦´»¼·ÂÀÃÈÇËÊÇÅÄÁÁ¿½»»¼¼¹¶²¬«¬±´º½º´°ª¦§±¨‡‹’¡£§©¨©«´»¼º»¼ºº¹º¼½¾ÁÂÆÆÊÎÑÑÑÐÎÊÊÍÏÍÌÎÍËÊÉÉÈÄÆÉËÌËËËÊÊÊÊÉÉÇÅÅÅÄÄÄÄÅÅÄÄÅÅÅÃÀ¾¼¼¼¾¿¿¾¿¿½½»¹¹¸·¸¹¹»ºº»¸·µ´³´³´”0xµª§¥¥§¨¨¨¦§§¥¤¦¦¤¥vXzvT^ ,C¢¤¡ˆ_;.2Mu•¥¥ª®²µ·»¼½ÁÂÂÃÂÀ¿»¶¯©£¡œš›£¨®²´¸»½¾ÀÀÀ¾½¾¾½¾¿ÁÂÁ¿À¿¾»º¹º»¾ÀÃÈÊÍÐÓÕÕÔÒÎÊÅÀ¹³®¨¦¥£§¬¯±±²²µ¶¶³®¨£§«¦Ÿ›¦µ»¼º·²¢†ƒœ§¯´µ´¯¤¡§µ¼½¸°¹µ³¼ÁÄÆÈÆÃÂÀ¾¾»ºººº¶³±¬©¨©®´¹¼¶±¬¨¥§±—‰™ ¢¦©¨©«³º¼½¼»»»ºº½½¾ÀÀÄÇÉÎÑÑÐÑÎÌËÌËÌÍÍËÊÉÉÈÉÆÆÈËÌÍÍËÊËÌËËÊÉÈÉÆÅÅÃÄÅÆÅÃÄÄÄÃÁ¿¾½¿¿À¿¿¿À½½¼ºº¸¸¹ºººº»»¸¶¶´´µµº“2‚¸®«¦¤¥¦¦¦¤¤£¡£§¥¤¤qЧ¦ŒW™ƒ'LŸ›¡¡ŽjI93Cjž°µ¸»¼¾ÀÃÃÂÁÀ¿½¸³¦¡ž››ž¦«°´¶º½¾¿À¿À¿½½¾½¾ÀÂÂÁ¿¾½¼º¹»¼¿ÂÄÇÌÎÐÓÕÔÔÓÏËǼµ°ª¥¢£¤©®²³´³µ¸¸¶³®¦¢¨ª¢™š«¸¼»¸´°ª˜€…“¢ª®®¯³²µµ¬¢¡¥¬¶¼¿º²¶´²±·¼¿ÅÆÄÂÁ¾»½¼º¹¸µ²²¯«§¦¨¬¯·¹´®¬§£«®Š˜ ¢¤¨ªªª³º¼¿¾¼»»»»¼½À¿ÀÁÅÈÍÏÐÐÐÍÌËÌÌËËËËÉÊÊÉÈÇÇÇÉËÍÍËÊÌËÊÌËÉÉÉÉÅÅÅÅÆÆÅÄÄÂÃÁÀÀ¿¿ÀÀÀÀ¿¼¾¿¿¿½¼ºº»ºººººº¸¶¶µµ´´¼ˆ3ˆº°ª¦¦¦¥¦¤¢¡Ÿž£¤¤£ £Ÿ››ƒ$W šš› ¢žŠmR.T¶¶»½¾ÁÃÄÃÂÁÀÀ»¶±«£Ÿœ›ž¤«¯³¶¸»½¿ÁÀÁÀÀ¾½¾¿ÀÂÂÃÂÀ¾¼¼¼»¾¿ÁÅÇÊÍÐÒÓÓÔÔÐÍÈÿ¹±«§¤¢¢¤¨¬°³¶¶¹¹·¶³¬¤£ª§¡™¡²·¹¹¸´°¦†ƒ‡˜¤«®¯³´µ¸´«¢£§¸¾¿½²º²°¯±µº½ÂÃÃÁ¿»»»ºº¹¸³±¯¬§¥¤¥§¯³¶³¯«¦¨´ª‡”¢¥¦©«©ª±·»¾¾¾¼ºº¼»¾¿ÁÃÃÆÈÍÐÏÐÎÌËËÌÌËËÊÊÉÊÉÉÈÇÆÇÊÊÌÌÊÈÊÉÊËÌÊËÉÈÇÇÇÅÄÅÅÄÅÄÄÂÀÀ¿ÀÀÀÀÀ¿½½¿¿¾¿½º»»º¹¹¹¸¸¸··¸¶´²¼5Œ¹°®«ª©§¥£¦x𣢤£¤£¡žœœž€ ^£œœ››i‡¬¥X/˜º¹¼¿ÁÁÂÄÃÂÁÁ¾¹³®¨¢œ›¤ª¯²µ·º¼¾¿ÁÀÁÀÀ¼»¼¾¾ÀÁÀ¿¿½½½¼¼¾ÁÄÆÊËÎÐÒÔÔÔÑÎÊÆÂ¾·¯©£¢¤¥¦ª®°´·¸¸¹¸µ°«¥£¨¦œ™©´·¹¹·³ªš€‡œ¨¬°´´¶·²¨ ¤©¯ºÀÀ¼°Á´¯°°±µ»¾¿ÀÀ½»¹¸¹»º·¶³°«¨¤ ž ¤«²·µ¨¦¬¸™Œž¢¥©«ª¨®¸»¼¾¾¼¹»»º»½ÀÃÅÇÊÍÎÍÎÏÍÊÉËÍÊÌÌÉÉÊÊÊÇÇÇÈÊËËËÊÈÉÉËËÌÍËÊÉÉÇÅÄÃÅÄÃÄÄÄ¿¾¿¿À¾¾¿¿¿¾¾¾¾½»¹ºº¸¹¸¸¹¸¶¶¶¶¶µ±¹s2’·°¯¬«©¦¤¦—G#L—¥£¤¤¤¡Ÿžœt g¤›œœŸi1t¯¨³o0”¼º½¿ÀÁÁÁÁÁÀ¿º´°ª¦ œ¢©®±´·¹»¿¿ÀÁÀÁÁ¿»º»¼¼½¾¿¾½½¾¾¼»½ÁÄÆÊÍÐÓÓÕÖÒÎËÇþº²ª¥¢£¦¦¨¬°²¶¸¹¹¹¶´®ª¢ ¤ —°µ¹¹·¶°§Ž‡”¢«°³¶·¶°¤Ÿ¤¨°»ÁÁ¹±Éº±°±°²µº¼¾¿¿¼¸··¹¸¸¶µ²¯©¥žšš¡§±¸µ¬¨§¯°‘›¢¥§ª«¦¬¸¼¿¿¾½»»¹º»¼¾ÁÄÇÊÌÍÍÏÏËÉÈÈÊËËËÊÉËÊÉÈÆÈÉÉÊÌËÉÉÊÉÊËËËÊÊÊÊÈÆÆÄÄÄÄÄľ¾À¿¿¿¿ÀÀ¿¾¼¾¾¼ººº¹¹¹¶¶¹¹·µµ¶µ³±¸j.—´°¯®«¨¥¥›B!)#JŸ¤¥¥£ žŸž n"r¤£zOXw±¬²Z;¬½»½¿ÀÂÁÁÂÁ¿»·²¬§¢Ÿœž¤¬¯³··º½ÀÀÀÀÁÁÁ¼»¼¼»½½½½½½½½½¼¼¼ÀÄÇÊÍÑÔÔÕÓÏÊÉÄÀ¼µ®¥¡¡£¦§©«¯²µ¹º»¸¶³¬¥ Ÿ œ™¨¶¹¹¶´±¬‚‚Œ™¥©¬®²¶·¹´£ £«´¾À¿¸¯Ïµ²±°±´·¹º¾¿½¹¸·µ´¶¶µ³²®¨¡™—˜š£¯¹¹¯¬¶¢•££¦ªª¨¬¸¼½¾½¼½º¹»¼½¾ÁÄÇËÌÎÐÑÏÌÉÊÉÊËÊËËËÉÈÉÈÇÈÉÊËÍÌÉÊÊÉÊÉÉÉÈÈÉÉÊÉÇÅÄÃÄÄÃÁ¿¾¾¾¿ÀÁÀ¿À¿¿¾¾¼»»»ºº¹¹·¸»»·¶¶µ³²±¶b/𴝱°°©§¥M'()%dª¤¤£ ¡¡Ÿ¢j"}¥ž¤‹EŒlwµ²¥?`½½¿ÀÂÃÂÁÀ¿¼¸´©¦ žž¢¨¬²µ¸¹»¾ÁÂÁÀÀÀ¾»»½¾¼½½½½¼¼¼»»º½½ÀÃÈÌÎÒÓÔÓÐÍÈÅÿ¹±¬¥¡ £¥¦©¬°´¶¹º¹¹¶´¯¨Ÿœœ˜°¹»¹·´¯¥Œ‚ˆ‘ž¤©¬¯´¹¸¸´©¢Ÿ¤®·¾Á¾¹°ÏȺµ´²´µµµ¸¹»»º¸¶µ´³³µ²³°«¦ ™–““›¡¨µ»·³¶¶› ¢¥¨©¦ªµº¼¾½»»º¹º½¿ÂÃÅÉÍÎÎÐÐÎÌÊÊÊÉÉËÌÍÍËÉÊÈÈÈÇÉËÌËËËËÌËÊÉÈÈÉÊÈÈÉÇÆÃÁÁÂÁÁ¾½¾¾¿ÀÁÁÁÁÀÀ¾½¼»¹¸¸¹¹¹º»ºº·µ¶µ³³³·^4³®°°°¯ª¬u%&&'&/•¥£¤££ Ÿ¥e'…¥¦•IEzXy´·|5•ļ¿ÀÂÃÃÃÂÀ¾º´®©¤¡ŸŸ¢§¬²¶¸»¼½ÀÃÄÂÀÀ¿½º»½¾¾¿¾½½¼¼¼¼»»½¿¿ÃÊÎÐÓÓÒÐÍÈÅÃÀº³®¨¥¡¡£¤¥©¬±¸º»»¹¹¶³¥›š™£³º»¹·³«œ‚‚‹–¢¦¨±¶·¹¸±¤¢Ÿ¥°¹¿Á¿»°ÐÍÀ·¶¶¶´µ¶¶·¸¹··µ´µ³²²³´³°«¥Ž’–›¥³½¿¾¹¦› £¤§§©´¹»¼¾¼»¼¼»¼¿ÃÄÅÊÍÎÏÐÐÏÍËËÊÉÉËÍÎÍËÊÊÈÈÈÈÇÈÉÊÌÌËÌËËËÊÈÉÉÈÇÇÇÅÄÂÂÁÁÀ¾¾¾ÀÁÀÀ¿¿ÀÀÁÀÀ¿¼¹¹ººººº»º¸¶´¶µ´´´¸[9¡±®¯¯°®«¥B%)'&'++y¬¤¥¤£¢Ÿ¥^-§¦dm…g@}¸µPJ¹ÁÀÁÂÃÃÃÄÂÁ½µ°ª¦¢¡Ÿ¡¥ª®´·º¼¾ÀÃÄÄÃÁÁ¿¼»¼¾¿¿ÀÀ¾½½¾½½½»»½ÁÅËÏÑÔÔÑÏÊÆÃ¿»´°«¥¢¡¢¤§§¨«²·ºººº¸µ²«£›™—™©¶¹º¸³¬¦“…™¤§ª¯³¶¸º·¯¦¡¢©³º¿ÀÀ»¯ÑÏĸ··¸·¶µ´´¶¶µ³³³²³³²²²´³¯¦¢”‰‰Š‘•ž½Æ¿«˜ž¡£¥¤§²·º»½¾¼½½¼½¿ÂÅÇÉÌÍÏÐÑÎËËÊÉÊÊËÌÌÊÊÊÊÊËÊÊÉÈÉËÍÍËËÌËÌËÉÈÉÊÈÈÇÅÄÂÂÁÁÁÀ¿¿ÂÃÂÂÁÁÁ¿ÁÁÁ¿»¹º¼»»º¹¹·µ´¶·¶´´´¶X>£¯®®®¯¯Ÿ9,+)))0.v¬¦§§¥£¢§Y2–¨¢—®±µq‚½Ÿ8}ÇÂÁÂÃÄÄÄÃÁ½¸±©¦¢ Ÿ¥©¬°³·¹»¿ÃÃÅÃÂÀ¾½»»½¾¿ÁÀ¿¾¼¾¿¿½½¼¾¿ÄÉÍÏÒÓÒÐÎÈÄÁ¼¶±¬§£ ¡¤¦§§©ª²·¸º¼¹·´¯ª¢™–“ž±ºº¹µ±«œ„‚‡’ž¦§¬²¶¶¸¸µ¬¤œ£µ¼ÀÀ¾¹°ÒÒʺ¹¸»¹·´³³³²²¯¯±³³´³±°±³²¬§ –Іˆ‰“•Ÿ´«—™ ££¢¥¯¶¹»½½½½¾¿ÀÀÁÅÈÉÊÌÎÎÏÌËÊËÊËËËÊÊÈÉÈÉÊËÌÊÈÇÉËÍÎÍËÌËËÊÉÇÇÉÉÈÇÅÅÅÃÁÁÂÀ¾¿ÂÄÃÃÂÁÀÁÁÁÁ¼»ºº»½»¹¸¸µµ···¶´µ¶µQB©¯«¬¬ªZ04*+57F—§¦§¦¥¥¤§T6ª®³”›½lA®ÃÀÀÁÂÃÃÂÁ¾¹´¨¤¢ Ÿ¢¦«¯²´¶¹¼¿ÂÄÄÃÀ¿¼¼»¼½½¾¾½½¼»¼½¿½¼½ÀÄÇÌÏÑÑÐÏÎËÈþ¹³®©¥¡ ¢¤¦¦§ª³·º»º¸¶²®¨¡™“•§¶»»¹³¨’€†‹š¢§«°µ··¹¸²§š¢°¹¾¿À½¸®ÏÒξ¸º»º¶µ´³²³±¬®±±³²°¯®®±¯¬©Ÿ‘†„‡ŠŒ•›—–ž££¢£¶º»¾¼¼¼¼¿ÀÀÂÄÈÊËËÍÎÎÌÉÇÉÊÉÊÉÊÊÉÉÊÊÉÊÌÌÈÈÉÊÌËÍÍËÊËÊÈÈÈÇÉÈÆÅÅÄÂÂÁÂÁ¿ÀÀÃÃÃÂÁÁÀ¿¾¿¿¿¾½½½¼ºº¹··¸¹¹¸¶¶µ²LI®®¬¬ª®œme=1bm¥£¦©¨¦¤¤¦O<¤¯¯¯±²³·ªAjÆ¿ÀÀÁÂÂÂÁ¿¼·±«§¢¡ŸŸ¡§°³¶¸º½ÀÁÂÂÀ½½»ºº¼¾¾½»º¹ºº»»¼½½¾ÂÅÉÌÏÑÒÏÍÍÊǽ¶¯ª¦¢ ¡¢¤¨¨¨«°µ¹º»º¸µ±¬¦ ˜“¯¸¸¸µ±©Ÿ…‚Š–Ÿ¦©±¶··¸µ©Ÿ˜ž¦°·¾Á¾·¬ÏÐÏź¼»ºº·¶³²²°«©ª®®¯®«¬®°®¬§‹‚ƒ…„‡”–‘œž¡ ¡¨³¸»¼»¼½½¿¿¿ÁÅÇËÍÎÎÍÍÊÊÈÉÉÊËÊÉÉÊËÊÊÊÊÌÌÊÉÉÊËËÌÍÌÊÊÊÉÈÈÈÉÈÆÃÂÁÁÁÁÁÀÀÁÀÂÁÁÁÀÁÁ¿¾¿ÀÁ¿½½½½»»»º¹·¹¹¹·µ²¯FO°®®°¯¯¬°³¤H7–£ ¥¥§§¥£¦ªLB«±±°±²³µ½‡:›Ä¾ÂÃÂÃÄþºµ°ª¦¢¡ ¢¦«°³¶·º¼¿ÀÀÁ¿½¼¼¹·¹»»º»¸·¶¸¹º»»¼½¿ÄÇÊÌÎÐÑÐÍËÈÄÀ¸±¨£ŸŸ¡¢¥¨§¨¬´¸ºº¼»¸µ±¬¦ž””¦µ¹·µ²¬¤“‚…–¡¨¬¯µ···µ®¤œš ¨®¹¾Â¼µ«ÎÒÐȼ¼»ºº¹·¶µ³®©¤¢¤©«««ªªª¯¯ª£—‡€‚ƒƒ‡Œ‘Œ”›œž¨²·»½¼»»½¿¿ÀÂÅÇËÎÐÐÏÍËËËËÊÊÊÉÉÉÉËÊÉÈÊÍÍËÉÊÊÉÊÌÍÌÌÉÉÉÈÈÇÈÉÅÃÂÁÁÁÁÀ¿¿¿À¿¿¿¿ÀÁÁÀ¿¿¿¾½¼½½½¼¼¼»¼·µµ´³²±§;Q²¬®¯¬¬¬®ˆ>8}§ £¤¦¨¥¥§ª®IC¯²²±²´¶¸¸XV½À¿ÂÂÃÃÅÂÀ¼µ°¨¥¢¢¡¤ª±´·¹¼¾¿¿ÀÀ¿¾¾»ººº»ºº¹¸¹··¹ºº¹»»¾ÂÆÊÍÏÑÐÐÍÉÄÁ»µ±«§¡žž ¡£¦§¨¬³¶¹¹»¹¸¶²«¤ž–¬¶¸¶³°¨ž‡…†š ¦¬´¹¹·¶³¬ ššŸ©±º¿À¿¼´ªÏÏÏÊ¿»¼»¹¸¹·µ²© Ÿ £§©¨¨¨«¬®°±¬§ ’‚}|€„‰ˆ™™™›§²·»½½½¼½¿ÀÂÃÄÇÈÊÍÑÏÌÌÌÌÌÍÍÌÊÉÉÊÉÉÉÆÈÊËËÈÊÊÊËËÍÌÌËÊÉÈÈÈÉÈÆÄÃÂÂÁÂÁÀ¿¿¾½¾¾¿¿ÁÁÀ¿¿¿¾½½½¿¿¿À¾½¼¸·¶´²±±¥7W¶®¯°±¯®®¯—|z¦¢£¥¦§¥¨«®¯GJ³²³³µ¶¶¼›;†ÈÀÂÃÅÄÃÿ»²¬¨¥¤£¢¤©¬¯´·¹º½¾¾¿À¿¾¿¿¼¼»¼¼¼º¹¸¹¹¹º»¼ºº½¿ÃÇÊÍÐÑÑÎËÅÁ½µ°¨¥ Ÿ ¢¢¢¤¦©®´¶¹ººº¸µ°©£›¡´¸¹µ±¥‘„Д𠦱·¸¹¸¶±¦ž››¡«³¼¿¿¾¼´¨ÎÍÍ˺¼¼¼¸¶µµ²¬¨¢ Ÿ ¡¡ ¤¨«®°®¬© {|~€†Œˆ‰–™–—¢°¶¹¼¼¼¼¼½¿ÁÃÄÆÈÊËÍÏÍËÌÌÌÍËËËÈÈÈÈÇÈÇÉÊÉÉÉÊÉÉÊÌÍËÊËÊÈÉÊÊÊÉÈÄÄÃÃÃÃÂÀÀÀÁÀÁÁ¿¾ÀÀÁÀ¾ÀÁÀ¿¿¾¾¿À¾¾¼¹¹¸µ´´µ¤5c¹®°±²¯®°°°²µ²ª¥¥¦§©¨§©¬±°EP¶²²³¶¶·¿oE³ÅÂÃÄÆÆÅľ·¯ª¥£¡£¥¦«°²µ·¹¼¾¿ÀÁ¿¾¿¾¾»»»»»»¹¸¹º¹º½¾½¾ÀÂÅÇÈËÎÑÒÏÌÉÄ¿º±¬©¦¤ ŸŸ¡¢¢£¤©¯³·¸¸¸·µ³§¡™˜«´¶´²®¦œƒŒ—¤«·¸¹·¸´ª¢™šž¥®¹¾¿¾¾¹²¨ÑÐÒ̹¼¼¼¹·µ´°«©¥¡¡—•——›ž£¦ª®°±¬¢‹~zz|}€ˆ‡…“”““Ÿ®µ¹º»½½¼»¼¿ÁÂÅÈÊÌÍÍÌËÌÌÌÌËÌËÈÇÈÇÆÈÇÉÊÊÊÊÉÈÉÈÉÊÊÉÉÉÉÉÉÊÊÉÈÇÄÄÄÄÃÂÀ¿ÀÁÂÀ¿¿¾ÀÀ¿¾¿¿ÀÀÁ¿½¾¿¾½¾»¹¹¸¶´µ¶ 6p¼ªª³²¯¬°°°²¯©§¨©©©¨¦§«²¯AWº³²´µµº²DlÇÁÂÃÅÇÆÅÃÀ»´¯ª¦¥¤¤§©±´¶¸¹¼¾¿ÀÀ¿¾¿¾»¸¹º»»ººº»¼¼¼¿¿¿ÂÆÇÈÉÉÌÐÔÒÏÌÇÁ»µ°«§£¡Ÿž ¢¢¦«°³µ·¸¸µ´²§ šž®²²±±©¢{‚„Œ”¦²·¸¸¸¶±§žšœŸ¨µ½¿¿½»¸±¦×ÔÓÏȼ¼»··¶´²°¬©¥¢¡œ”Ž”™œ¡£¦©¯°¯® ‹yyy{|~„’ž¬±µ¹º½½¼¼¾¿ÀÂÅÈÊÏÏÎÍËËËËÌÌÍËÉÈÇÆÆÆÈÉÉÉÉÉÉÊÉÈÉËÌËÊÉÉÉÉÉÊÉÇÇÆÇÆÅÅÃÁÀ¿ÁÀÀ¿¾¿ÁÁÀÀÀÀÀ¿À¿¿ÀÀ¿¿¿º¹¸¸·¶¶¹š2yÁ™m¹³²}£±¯°°°®«¬«¬ª©¨§ª¯³<^¸²²µ¶¶Â8¢ÈÄÄÆÇÇÅÃÁ½¶°¬©§§§§«¯²¶¹ºº¼¿ÀÀÀ¾¾½¾¼»¹º»»»»¼¼½¾¾¿ÂÁÂÅÇÈÊÌÍÐÑÒÐËǽ·±®«¨¤¡ŸŸ Ÿ ¢¤§¬°³´··¶´²°¬§¡œ¥®°°°¬¥™…€ƒ…Ž˜¢®¶¹¸¸ºµ¬¤œœ¡ª¸¿¿À¿»¶®¥ÚÖÓÐʽº¼ºµ´²°®©¦£¢œ•Љ‹‹“•–ž £§©¬ª§œ†{yz{z~|‡Žª¯´¹¹¼¼½¾¾ÀÁÁÅÊÍÐÒÑÎÌËËÊÊÊÉÉÇÇÆÅÆÇÇÉÈÉÉÈÉÊÉÉÊËÌËÊÉÉÈÈÉÉÈÇÆÆÆÆÆÅÂÁÁÀÀ¿¿À¿ÀÁÀÀÀ¿ÁÀÀÀ¿¿¿ÀÂÁ¿»º¹º¹¸¸¼”.|Á²Ro‚wc²±±²²°®¯¯®®«¬©§®³¶ª;c¶±³·¹½Á]ZÈÅÆÇÈÇÆÂÀ¾º³©©¨§§«®²¶·¼»¼¿ÁÀÀ¿¾¾½¼»»»»¼¼¼¼¼½¾½¾ÂÄÃÃÅÇÉËÌÎÎÏÏÌÇÁ½·±®«©§¢ž ¥¥¨±´´´¶µ³²¯«¨¡¦®®¬¦¢€„†Œ•Ÿ©²¸¹¹¹·¯¥Ÿœ›œ¤®¹¾ÀÀÀ»´«¤ÜÙÓÎÈÀ¹º¹·´±¯«©¦¤¤ ˜‹ˆ‡ŠŽ“——›Ÿ¢¡¤¢•}yyyxywŠŒŽ™©¯´·¸¹º½¿ÀÂÂÂÆÉÍÐÑÑÏÎÌËÌÊÉÉÈÇÆÆÇÇÈÇÉÈÉÉÊÊÊÉÉÊÌÌËÊÊÊÉÉÉÉÈÇÅÅÅÆÆÅÂÁÂÂÁÁÁÁÂÂÂÁÀÀÂÂÁ¿¿¿ÀÁÁÁÁ¿»»»»ºº¸½“.ƒ¿¼iPsBй²²³²±°±±¯««©«±³µ¦7f¸²¶¹¼Ä¨A“ÌÄÆÆÆÅÄÁ¾º¶°©§§¨©ª¯±´µ·»»¼¾¾¿Á¿¾¾¼»ºº¹»¼¼¼»º»¼½¾ÁÃÃÄÆÇÈÊÊÌÍÎÎËÆÀ»µ°¬©§£ œ›œž¢¥¨¬¯²³³³´³³±«¦¡Ÿ§«¬©£™…€ƒŠ‘›¦¯µ¹¹¸·°¨¡œš›ž§³»ÀÀ¿¾»´ª£àÝÖÍÆÀ¹¶µµ³¯«ª§¥¤¥¢˜††…ˆ‰ˆŽ’˜—Œ|yyxvsz†ˆ–§®±µ¶¸¼½¾ÁÃÄÅÇÊÏÒÐÏÏÎÍÎÎÌËÌÌËÊÇÇÉÉÈÈÇÇÉÉÉÉÈÇÉÊËÊÊÊÉÉÉÉÉÈÅÅÆÅÄÄÄÁÁÁÁÁÁÁÂÂÃÃÂÂÃÃÃÂÁ¿¿¿À¿¿½¼»¼¼¼»»¸¾Ž/ˆ»¸dž_ª³³²±¯°±²³²¯««®²³µ¡2h¸²·½¾ÄpRÀÈÅÃÅÆÃÁ¾¾º³®©¦§¨ª±²µ¶·º½¾¾½¼½¼¼¼ºº¹¹¹º»º¹º»º½¿ÀÂÃÄÅÆÉÊÊËËÍÍÊÇý·±¯¬¨¥¢ž›››œŸ£¦ª®°±±²²²±²±®«¦Ÿ §©ª«¥Ÿ‚…Œ”¡ª²·¹¸¸³¬§Ÿœ›œ¡ª·½À¿½¼¹±©£ÚÙÔËĽ¸¶¶´²°®¬«¦¢ ¤¤ 𓉄ƒ…ˆˆ‡‡‡‚€~‡‹Ž„{yxrlxƒ„Š”¦¬°´º¼¼¾ÀÂÃÆÉÌÏÒÒÐÏÏÏÎÎÍÍÍÌÌÊÈÇÆÇÈÈÇÆÇÉÉÈÇÇÈÈÈÉÊÊÉÈÉÈÉÈÇÆÆÆÄÄÃÀÀÁÁÁÁÁÂÂÃÄÃÄÄÅÄÄÂÀ¿¿ÀÀ¼»»»»»»¹¹·»Š.ޏµªQa~·±²²°®¯°²³³²°°¯²´´±˜0l¸´º¾Â°EˆÍÄÅÃÅÆÃÀ¾»·²ª©©ª¬¯²µ·¹»¼½½¾¾¼»»º»»º»¹º¼¼½»»½¼¾ÁÃÄÅÆÇÈÊËÌËÌÌÊÇÿ¸³©§¦¤ šš›œ¡¥¦ª®¯°²³²±¯°¯©§Ÿ §¨¨¨¡˜…ƒ‰’œ¤®¶¸·¸¶°«¥ž››ž¦°¸¾ÁÁ½¼·¯¦£ÉÉž»¸¶·´±¯ª¦¡¡¢¢¡œ–Œƒ€ƒ…††„€{yxwx~€|vvtlv„‚†”¤««°µº¼½¾¿ÀÂÆËÏÐÒÓÑÏÎÏÎÎÎÍÌÊÉÉÈÈÆÆÇÇÇÈÉÉÉÈÇÈÉÊÉÉÊÈÈÈÇÈÇÆÆÆÆÆÄÅÁÀÀÁÁÁÁÁÃÃÃÃÃÃÅÄÅÄÁ¿¿½¾¾»»»»»»º¹º·¼ˆ-¹¯µ_@¨µ³²²°¯±±²³³³³´µµµ²²+r¸¸¾¾ÇzL¸ÅÃÄÅÅÄÿ¼¸µ²®ª«¬¬¯²µ¹º¼½½¿¿¿¿¿¾¾¼º»½½¼¾¿¿¿½½½¾¿ÂÄÆÇÈÉÊËÌËËËËÉÅÁ¼µ¯©¨¤¢Ÿ›˜™ž¡£¥¨¬¯±±±²°¯«©¤ž ¤§§¤Œ‡Œ˜¡«µ¸···´°© š™› ©²º¾¿¿¿¼´ª¤£¼»¹¹¹¼»µµ²°®¬¬ª¦¢¢ žš•†€€ƒ…ƒ€{wtstwvwvtrojq‚ƒ‚¡ªª®´·»¾ÀÀ¿ÂÅÊÎÐÑÑÎÏÎÍÍÍÌÌÌËÉÈÇÆÇÅÆÈÈÉËÊÊÉÇÈÊËÌÊËËÊÉÇÆÅÅÅÅÅÅÅÄÁ¿¾ÀÁÁÁÂÂÁÁÁÁÂÀÁÂÁ¿¾¾½¼½½¼»¹¹»º¹¸¶¹‚-髆t³®®°¯¯°¯±²³³²´¶´³²´Š,~À»¿ÀµItÇÁÅÆÇÆÃÀ¼º¹´°¬ªª«±´¸¹¼¿¿¿ÁÂÁÀ¿ÁÀ¿½½¿¾¿ÁÀÀ¿¾¾¾ÀÂÅÅÇÉÊËËÌÌËÉÈÇÆÄ¾¹±«©¥¢Ÿœš˜šŸ¡¤¦¦©¬®®¯±±°°®¬¬«£ž¢¦¤ –†€ƒ‰“œ¦°µ¸µ·¶³ª¢œšš¥·¼¿¿¿½º±¥££³±³³µ·º·°°¯««©¦¡ Ÿœš˜—”Œ€‚}ytrrrrqoqpmifm‚‚‹Ÿ¬©«±·¼¾¿ÀÁÄÅÈËÎÐÏÏÎÌËÌËÊÊËÉÆÅÆÇÆÆÇÈÈÈÊÊÉÈÈÈÉÊËÌËËÊÊÈÆÇÇÆÅÅÅÇÅÂÀ½¾ÁÂÁÂÂÁÀÂÁÁÀÁÁ¿¾¼½½¼½½»º¹¹»¹¸¶³´’.W§«©¬±°®°°®¯±²³´²±±±³²²¼‚2–Á»½Å–:£ÈÅÆÅÅÅþ»¹¸´°ª©ª±µ¹¼¾ÁÁÂÃÂÃÄÄÄÄÃÃÃÁÀÁÂÁÀ¾¿ÀÁÄÅÇÆÇÈÊÌËËËÊÇÆÄ¿¹²¬¨§¤¡š˜™›œŸ¢£§ª®®®¯²²°°®««§£››Ÿ¢¡™‚‡™¡¬´¶¶¸¶³¤žœ›ž¨²¹½ÀÀ¿¼·®¤¢¢®®¯°²µ¸·±«ª©©¥Ÿœžœ˜““„}€}vtrppnkkmkihch{€ƒŠ›«««¯´¹¼¿ÀÃÅÇÊËÍÏÐÏÌÍÌÊÉÉÈÉÈÅÅÅÄÅÇÈÈÇÈÊËÉÇÇÈÉÉÊËÌËÊÉÇÉÈÈÈÇÅÆÆÅÂÁ¿¿ÁÂÂÁÁÁÁÁÂÃÁÁ¾½½¼½½¾¼»ºº¸º»¹¸¶´²¯T'BWcltz‡”› ¥§©©®¯°¯¬¯®® RI³À½½ÆmLÀÅÇÆÅÄÿ¼º·µ²®«ª¯²¶»¾ÁÄÄÅÇÅÅÇÇÆÇÇÆÅÃÁÂÅÃÁÁÂÂÄÅÅÆÆÈÇÉÊËÊÉÈÅÅÁ¾º´®¨¥¦¢Ÿš™˜šœ £¤¨¬®¯°¯®°±°°®¬©§¢™œ ¡›ƒ…‡’¦²·¶·¶³®¦¡ž››¢¬´»¾ÀÀ¾º´©¢¢¥®®®®¯²µ¸´«ªª¨¥ ™š››˜“‡€}~~|zxuromkjihggeaev~‚Š›¥§ª±µº¿ÀÃÅÉÌÎÐÑÑÎÌÎÍËÊÉÉÈÇÅÄÄÄÄÆÇÉÈÆÉÈÆÆÇÈËËËÊËÌËÊÈÊÉÉÉÈÇÇÇÇÃÂÁÁÂÂÂÂÁÀÀÂÂÂÀ¿¼½½½¾¿¿¾ººº·ººº»¹µ²µ¦eD;7630*0348;<?DEKPROLQSSDE‘ÃÀ¿ÁµIwËÅÆÅÅÄÃÁ¾»¹¶´±¬¬¯³¶º¿ÂÅÆÇÇÈÇÇÆÆÆÇÈÇÅÄÃÂÄÃÁÂÅÅÆÇÈÇÇÈÇÉÉÊÈÇÄÂÀ¾¹´¯¬§£¡Ÿ›š™™››ž¢¤¦«®®®¯°¯®®©¥ ™›–Š~ƒ…Œ—¢®³¶¶¶µ°¦¢ ›šŸ©²·¾À¾¾»¶°§¡¢¨¯¯®®®°±¶¶©¨§¥¢žš˜˜–•––‘Œˆ€|}}y{ywspmjiigefb\`r{€ˆ˜¤§§«¯²¸½ÁÄÆÈÌÎÏÑÑÏÍÍÎÎÌÊÈÈÄÃÄÃÃÄÆÇÈÇÅÈÈÇÇÇÈËËÊÊÌÌËËÊÊÉÉÇÇÇÉÈÉÆÄÂÅÄÃÃÃÃÁÁÂÁÀ¾½»¼¼¼»¼½½ºº¹¸º¹º»¹·µ²³±¤™’Ž‚`=243.6IOPOSWY]^ehy¦ÆÁ¿Ç@¥ÉÃÅÄÄÄÿ¼¹¶³±¯¬¯³µ»ÀÄÆÉÉÈÉÉÈÇÇÆÆÈÈÇÅÄÄÃÃÃÂÃÆÇÈÉÉÉÉÈÈÉÊÊÈÇÁ¿½ºµ±ª§¦¤ œšš››››ž¢¤§¬®¬«¬¯°°¯¬«©¥ž˜š›˜“ƒ~†œ©±µµ¶·²¨¢Ÿž›š£¶¼¿À¾½¹³¬¨£¢©µ±¯¯®®¯²·²¨¥£¡Ÿš˜——“‘““Žˆ~|{{zwsomjihggcZV\nw‡•£§¨©¬¯µ»ÀÅÇÌÎÏÐÐÎÍÍÌÌÍÊÉÆÄÂÂÂÀÂÃÄÆÆÆÇÈÈÇÆÇÉÊÊÊÊÊËÉËËËÊÊÉÉÈÉÈÇÆÄÅÆÅÅÅÅÅÃÂÁ¿¾¼¼»»¼½¾¾¾½»ºººº¹ºº¹¸¶¶´´µ·µ·º»´šuT?2>X‚Ÿ®¶¸··¸º¾ÃÇÂÂÃÂÅdZÇÇÆÆÅÿ¼º¸³¯¯¬ª«°´¸ÀÆÈÉÊÊÊÊÊÉÈÇÉÉÊÊÈÆÅÅÅÅÄÄÅÇÇÇÉÉÊÊÊÊÊÉÇÅÃÀ¾º·³¯«¦¤£¡œšššž ¡¢¤¨®¬«®±²±®«©§¥Ÿ•˜˜—‡}€„Œ•£®´µµ·µ¦£ œ™› «³¹¾¿¾½¼¹²ª§¢¥ª¹·³¯¬®®±·®¢Ÿš˜––”’‘’“‘†|{{zurplkifde`XQVlw}„‘ ¦§§©¬²¸¿ÆÊÍÐÐÐÎÌËËÌÊÈÆÄÃÂÀÁÁÀÀÂÃÄÅÇÇÆÇÆÆÇÉÊÉÉÊÊÉÉÈÉÊÊÌÌËÊÊÉÈÅÃÄÅÅÆÅÆÅÄÂÁ¿¼½¼ºº»»¼¿¿½ººººº¹¹¹··¶·µ³´³²³³³´¶·®•nO::GdЧ¶¸¹¹º»½ÀÂÂÆ¯EŠÏÆÆÆÄÁ¿¿¼¹·²¯««®±¶»ÂÆÈÉÉÉÉÉÊËÉÊËÊËÉÇÅÇÅÆÅÄÆÇÈÇÇÇÈÉÊÊÉÉÇÆÃÁ¿¼¹¶²®«¦¡Ÿ›™˜˜šœŸ£¤£¤«¯¯¬«¬¯°²±¬ª©¨£›“˜—’}…ˆ‘œ¨³¶³·¶²¬¨£žš˜ž¦°¶»¿À¾¼»·±¬§¥§¬»º·²¯°®¬«°¶§ ›š—”•”‘Œ‘’“Žz{zxuromlgdaa]XRPgv{ŽŸ¤¥¤¤¦¬°¸ÀÇÍÏÍÍËÉÈÈÇÆÆÃÀÀÀÁÀÁ¿ÀÁÁÃÄÅÅÆÇÆÈÈÊÈÇÇÈÊÉÊÉÈÉÉËÌÊÊÌËÉÅÄÆÆÆÆÅÅÄÂÂÁ¾¼¼»¼»º»»½½¼»¼º¹»¹¹¸¶µ¶µµµµ´´´´´µ¶¶¸»¼¯—wP;2;X{˜¯¼ÃÂÂÁ¾É{H¶ËÈÇÆÄÃÀ½º¶²²±¯®®³µºÀÄÇÈÉÊÉÈÈÉÉÉÊÊÊÊÈÆÅÆÇÇÇÈÇÉÈÇÆÆÈÉÉÉÈÇÆÄÂÀ½¸¶µ±®ª¥¡˜–——šœž¡¥¤§«¬©«®°±±¯«««ª¨¢˜“•”…|‚†˜¤´³µ¸¶°¨¤š™›¤¬³¹¾ÀÀ½»ºµ¯«¥£¨¯»¹ººµ°¬©§§¯²¢›–““’’ŒŒ‹Ž‘“‘Œƒ|xwuroojfca`\XQK^sx}ŠŸ¡¤£¢¤¤¥¬²¹ÁÅÅÄÃÀÀÀÁÁÁÀ¿½½¿¿¿¾¾¿ÀÁÂÂÀÃÄÅÈÈÈÇÇÆÇÈÈÉÉÉÈÉËÊÊÊËÌËÈÆÆÈÈÆÄÅÄÃÂÁ¿¾¾½½½»º»½¾¼¼½»¹¹¹·µ´³µµµµµ¶µ´µ¶¶··¸··º¿À· bJ:<GZoŒ£®·œC}ÐÈÈÆÅÅ¿º·²±²³²°¯³¸½ÃÇÈÉÈÉÈÈÈÇÇÆÈÈÈÈÇÆÆÇÈÇÆÈÇÇÇÈÇÇÇÇÈÇÇÆÄ¿¼¹¸²°«¨¤Ÿ›˜––—›› ¤¦©¬¨ª¬¯¯±±«««ª¨ –”’Œz~„Š”ª³³³µµ°©£™–𣩱¸¾¿¿½¼»¹³¬©¥¡©²¿º·¶³®«§¦¥¥¯°—”‘ŽŒ‹Š‹Š‘‰~yurqqnjeb`]WRLKYouz‹Ÿ¡¢£¢¢¡£¥©¯´·»¼¸·¸¹º»»½¼º¼¼¼¼¾¿¿ÀÁÁÁ¿ÀÂÂÆÈÇÆÈÆÇÈÈÈÉÉÈÉÌËËÌËÌÌÉÇÅÇÈÈÇÇÆÅÄÂÁÁÀ¿½½¼¹»»¹ºº¹···¸·µ²²²²µ·¸¸µµ¶·¶¶··¸·º¼¼¾Á¿³˜ƒoYIABGVLlÅÎÉÇÆÄ¿»¸µ´´µ³²®¯µ»ÁÅÉÊÊÉÉÈÈÈÈÇÅÆÇÇÇÈÈÉÈÇÇÇÇÇÇÈÇÇÈÉÈÇÆÅÂÀÀ¼¸¶³±«¨§¦£Ÿ™——™š›œž£¦ª¬«¬®¯¬«ªª¨¦Ÿ–“Ž}zˆ‘™¡¬²³·´°¨¡ ›˜˜Ÿ¦®µº¼½¾½º¹µ±¬¨¤¥®¸¾»·®©¨§¦Ÿ ¤¯ªš•’‘‹Š‰‹Š‹‹ŒŒ†€{vqoomida^[SLIGWqtw‡ ¤¢¡ ¡£¨ª«±±°°°°²µ·¹¸¹»º»¼¾¾ÀÁÁÂÁÁÂÂÃÅÆÇÈÆÆÉÉÉÉÈÉÈÈËËËÌÍÌËÊÊÊÈÈÈÇÈÇÆÅÃÂÂÂÁ¾¼»ºººº¹º¸¶µµµ¶´´´µ·¹º»»¸¸¹¹¸·¸¸¸¹¼¼¼¼¼¾ÀÂÅÉǼ³ž„v|™ÆÎÊÊÈÆÄÀ»·±±µ¶¶µ²®±¸½ÄÈÊÊÌÉÇÅÅÅÃÄÃÂÄÅÄÄÄÆÆÆÇÈÇÈÈÈÇÈÉÈÈÅÿ¿¼¸¶²°¨§¦¤¡ ™—™›œœž¡¦©¬¬®®°®¬¬ªª©¦¥¡“„wz€‹”ž§±µ¶³«¢Ÿ›™šŸ¤«²¶¸¼¾½¼»¸³¯©£¡¨²¹¸º¹¯¢ ž•—›œŸ¯™•““ŒŒ‹ˆ‰ˆ‡…‚{xtonliea]ZOIDCOptwƒœ££¢ŸžŸ ¡¢¢¨«¬¬¯²³µ¸¹¸¸»½½ÀÁÁÂÂÂÂÃÃÄÄÅÇÇÇÆÈÉÉÉÉÈÈÉÊÍÍÎÍÊÊÌÌËÉÈÈÈÈÈÈÆÅÆÅÃÂÁ¿¾¾½½»¹·¶µµµµ·¸¸»½¾À¿¾¼»¼»ººººº»½½¾½¾¾¿ÀÂÃÂÁÅÇÇÄÈÍËÊËÉÈÆÁºµ°°°±´µµ³³¶»ÀÆÈÉÉÇÄÃÁ¿¿½»º»½¾¿¿¿ÁÂÃÆÅÆÆÇÇÆÅÅÅÄÂÂÁ¿¿»·µ±ª§¥¤¢ Ÿ›˜—™›› ¢¢§ª¬®¯¯°°¯¬«©¨§¤“ŒywzŽ˜¤¬¯²µ´¯§ œ››¤ª¯³·º¿¿¾¼»¹´¬¦¡£«´¸³´³³©–’””’•°ž“”‘Œ‹‰‡‡ˆ†„~{xtqolhec]UMHA@Iluxƒœ¤¢¡ž››ž ¢¡Ÿ ¥¨ª««¬®°³µ´´¶·¸º»½¿¿ÀÁÂÃÃÃÄÃÄÅÅÇÆÇÈÉÈÊÊÊÊËÍÍÎÌËËËÌÌÊÉÉÈÈÈÇÇÇÇÆÆÆÇÃÀÁÀ¿»¸¹·¶µ·º½ÀÁÂÃÃÃÄÂÀ¾¾¿½½½½¾½½¾¿¿¿¿¿¿ÁÂÁÂÀ¿ÀÄÇÉÊËÉÆÄÀ»¶°®®¯±²³µ¹¿ÆÈÈÆÅÄÁ½»º¹·¶³³¶¸ºº»»¾ÀÂÃÄÅÆÅÄÃÃÃÃÃÂÀ¾º¸¶µ°«¨¥££¡ž›™™œ››› £¤©®°®®®«ªªª¨§£›‘…uw}‰’ž¦¬±´²°ª£Ÿœœž¥ª¯´¶»¾¾½½»º¶°«¦¤§°¶¸©©ª²¯£Œ‹Ž‹Ž’š¨µ¦˜’‘ŽŒŠˆ‰ˆ‡‡‚zvtplifcaZRJE=9@gvz…¤¢žŸ›žŸ ž £¦¨©©«¬¬¯²³³´µ·¸¸º»½½½¿ÀÁÂÃÄÃÄÅÆÇÇÇÈÈÇÈÉÊÌÌÎÎÎÌÌËÌÍÍÌËËËÊÊÈÇÇÇÇÇÈÈÆÄÄÄÿ»º¹º¹¹¼¿ÃÇÆÈÇÇÆÄÄÂÁÀÀ¿ÀÀ¿¾¾¿¾¿ÁÁ¿ÂÄÄÄÃÂÃÆÉËÌÊÈÄÁ½»·³°®ª«®¬®³·½ÁÇÆÄÂÂÀ»¹·¶¶µ±¯°²²µ¶·¸»½¾¿ÀÁÃÄÂÂÃÃÃÁ¾¼º¸µ³°¬§¤¤¡ Ÿžš™™œœ›¡¡£¥ª®¬®««ª¨§§§¥¡švtz‚™¢ª¯²²¯¬§¤¡ ¢©®³¶¹»¾¾½¼»º¸´°ª§§®´·¶¢¢¤§©§™‹…†‡‡‡Š“£°ªŸ•‘ŽŒ‹ŠŠ†„|uqmkida]WOD9407cz|…— ¢žœš›žžœ¡££¥§¦¨«¬°²³´µ¶·¸¸¹»¼¼¾¾¾¿ÀÃÄÄÅÅÆÇÇÇÆÇÇÈÉËÌÏÐÏÎÎÌÍÍÎÎÍÍÎÍËÊÉÉÈÈÉÈÉÈÆÆÇÄÁÀ½»¼¼¼¾ÁÅÆÊÊÊÊÉÇÅÄÃÂÁ¿¿À¿¿À¾¿ÂÁÀÀÃÄÄÃÄÄÅÈÊÊÉÇÃÀ¾»·´²²°®¯¯®±µºÀÄÅÄÃÀ¾º¶´³±²°¯±²³µ¶·¸¹»¼¾ÁÂÁÁÁÀ¾»¹¸¶³°¬¨¦¤¤¢ œ›™™›››Ÿ¢¤¦¨ª«¬¬¬¬¬«¨§¥¤¢Ÿ—ow|ˆ’§²±®©¨¦¦¨¬³¶¹º»¼¾½½¼º¹µ²¬§¥¬³µµ´›œžŸ¤¤‚€€„„†˜¨¨ž”’ŽŠ‹‡ƒ~ztplkeb\YQD:41.1Xx}†– ¢ ›šš›žž ¡¢£¤£¦ª¬¯²³´µµ¶¶·¸º¼¾¿¿¾¿ÀÂÄÄÄÄÄÆÇÇÆÇÈÈÊËÌÏÑÐÏÎÍÍÏÐÏÏÎÍÎËËÊÊÉÊÊÊÊÉÉÈÇÅÁÁÀ¿ÀÁÀÂÃÆÈËÊÊÉÆÅÃÂÀÀ¿¿¿¿¿ÀÁÀÀÀÀ¿ÀÃÃÂÁÄÆÆÉÊÉÈÄÀ¾º¹·¶µµµ´²²°°´·»ÀÄÅÄÃÀ»¶±°¬ª«¬©§¥§©¬¬®°²²´µ¶¸¸»¾¿¿¾½¼¹¶µ²¯¬¨¦¤¤¤¢ššš›œœ›œ ¢¥¨ªª¬¬®¬¬«©§¨¥¡Ÿ‘wty‚Œ–¤«¯¯¬¬«ªª¬°µ¶º½½¼½»»½»¸¶³°©¥§²´³²–”“‘•˜ €~{}~€„‡‘ ±¦•Ї|vsojfb\VME?;620/Jv}†”Ÿ¢ ››˜˜›ž ¢£¤£¥¦¨ªª®²²´¶¶·¶¶·¹½ÀÁÁÂÃÄÄÄÅÅÄÅÆÆÆÇÈÊÌÌÍÎÐÑÑÎÍÏÑÑÐÐÎÍÌÌÌËÊÊËËÊÈÉÊÊÊÈÆÅÅÃÃÅÇÇÉÉËËÉÈÆÃÁ¿¾¿À¿¿¿¾¿¿ÀÀÁÀÁÁÃÄÃÃÃÆÈÉÊÊÉÆÂ¾½¹¶µ´´´µ¶¸¶³³´¶»¿ÂÁ¿½¸³°«¨¥¥¥¤£ ¤¦¨¨©««®±´´µµ·º»ºººº·µ´°¬©¦¥£¢¡žœ™˜˜œž ¡¥©«ª«¬®¬¬©¨§§¤ žsu{…ž¨®¯¬¬¬¬®²¸»½¾¾½¼¼»¼¼º¹¹³«¦¦«°±²±°™Š‹Œ™•…~{{zz{~…˜¤°¯©“‰„}vrmgb^ZQIHDB?9736Dk}…šžŸœš——šœžŸ ¢¤¤¥¥¥¤¦ª¬¯¯±±²´´³³µ·»¿ÁÂÃÄÄÄÅÅÄÄÄÆÆÆÈÉËÍÍÎÐÑÑÏÎÐÐÐÑÏÍÌÌÌËÊÊËÍÎÌÊÊÌËÌËÊÊÉÇÈÉÊÉÉËÌÊÉÇÄÁ¿¾½¾¿¿¿¾½¾¾¿¿¾¿ÀÂÃÄÅÇÇÊÊÉÉÇÅ¿»¹·´²°®°²´´³´µ¶¹¼»·µ±§£¤¤¡ ž›œžŸŸ¢¥¤¦¦¦©«®®°±´µ¶¸¹·¶µ´±®©§¦¤¡žœœ›˜——šœžžžŸ¢¥©«««¬¬¬¬ª¨¦¥¤¢Ÿ›‡twŠ—£«®¬«ª¬«ª®³¸¼½½¼½»º»½½¼¼¹¶¬¦¦©°±±±°–މˆ‰‰Š–„|yxyz{|}ˆœ¦«®¦“upe`\WOIGHGCA@=;:Bdz‚Š˜Ÿœ›™——šœžŸ Ÿ¡£¤§§§©¨©¬¯¯²²²³´¶¶º½½¿ÁÂÃÃÃÃÃÄÅÆÈÇÇÉËÎÐÏÐÐÏÏÐÑÑÐÏÐÊÂÊÎÍÎÏÎÏÎÌÍÍÍÌËÌËÈÉËËËÊÉÊÊÉÈÄÁ¿½½¼¾½¿¿½»»¼¼¼¼¾ÁÃÃÄÆÈÉËÊÉÇÿº··´±®¬««ª«®¯³´¶·´ª¥˜–˜™˜•”‘Ž’˜š›ž £££¥¦§ª«¬¯±³µµµµ³²±®ª¦¤£¡Ÿšš™™™˜™žž ¢¤§ªª««¬¬«ª¬«ª¨¥¤¤£Ÿ›‡t}„ž¨¬¬¬ª©©©©®´¸º»ººº»ºº¼¼»»¶¯¦¥¥¦«®¯°¯¯“‹‡„„…ƒˆˆ}yxwvvxy{„‡—Ÿ¬®³®£‘}k_VOMIJIGEBA@=?Yr}ˆ–ŸŸš–˜˜˜˜–šžžœœ £¦¨ª«¨§©«¬®°±°±²´´µµ¸»½¿ÁÁÃÂÄÅÆÆÆÈÇÇÈÊÌÏÑÐÐÏÎÏÏÏÏÐÕ»]_lw‡²ÑÏÏÎÎÍÍÍÌÊÉÈÌÎÍÎÍËÊÊÉÆÁ¿½½¾ÀÀÁÀ¿¿¾¼¼¼»»¿ÁÃÃÄÆÇÈÈÈÆÄÁÀ»¸¸¶´±©¨§¥¥¤¤©¯±±¬¦¢–‘ŽŒ‹Š‹ˆˆ‹•˜š ¢£¦¨ª««¯²´´´¶µ²°«¨¤£¢Ÿœ™šš›š™™›ž ¡¥¨©«¬«ª«ª«©ª¨¥¤£ ™„y€š¨««ªª¨¨§¨ª®²´µ¶···º¹¹¹»º¶®©¥¤¤¥¨¬«¬®®”Œ†‚‚‚…Š…}wusvxxxz~‚‡ˆ“œ£ª°³µ¯¥œ‹xoeZRPIFDBPlz†‘žœ•”••”’•—››šŸ£¦©ª¨©¨¨§ª®¯®¯±²´µ¶¹¼¾¾ÀÂÂÃÅÆÆÆÆÇÈÈÊÍÎÐÒÑÐÏÎÎÏÎÎÎÒ«:8752†ÖÏÍÍÎÎÎÍÊÈÉÈ«¨°´µ·¸ÄÈÄ¿ÁÄÅÅÄÁÁÁ¾½½¼¼¼¼½¾¿ÀÂÅÄÆÉÌËÇý´²¯©©§¡››Ÿ¡£¡¡¤§¨¨ª¤žœ—‘ŠˆŒŠŠ„„Š“–™›¢¥¦ª®¯±µµ¶¶·¶²¯ª¨¦¤¡¡žœ›™™š›œ›œž Ÿ ¥§¨©ª««¨¨¨¨§§©¨¥¤¡ž›–z†”¥¬«ªª©©©¨ª«¬±²±´´³³¶¸ºº»¹²¬¦£¢£§««©«®®•‹‡ƒ~€€‚‚~xurssutuxy{{~‚‡‹’š ¦°³±±°¬¥Ž}jYOKPhuƒšœ›—‘’‘‘””–™™›Ÿ¤¨¨¨¨¨§¦¨©ªªª®°³µ·¹»½½¿ÀÁÁÃÅÆÇÅÆÇÈÉÍÏÑÑÐÐÏÎÎÏÏÏÎÓ™:?>?:–ÖÌÍÍÍÌÍÊÊÒºl><ABDWÂÆÈ‹exŸ¯¹ÁÅÇÃÀÀ¾½¿ÁÀÂÂÁÂÞœ†qbUHD?866732489<>VŸ¢†IFHLMKMLOT]ds‹‹‘—š›Ÿ¢¥§ª¬¯±´¶¸¹··¸´¯¬¨¥£¡ žœšššš›››œœžž¡£¤¦¦¦¨©©¨§§¦¦¥¥¥¤¢Ÿ›’|{¡ª©ª««¬¬«ª««¯¯¯°²·¹¸·³¨¥¡¡¢¦©©©«««™Š†„}z{}~€ztqppqrrsvuxxz~€‚‚ˆ•˜›œž žŸ™‚s_RQaq~Œ”˜›—’“•––˜™ž£¤¤¥¥££¥¥¦¥§«°²µ¸¹»»»¼¿¿ÁÃÄÅÆÆÇÇÇÈÌÏÒÒÑÐÏÎÏÏÐÐÏц;C>>>¦ÐËËËËÊÊÎÊ•E-:<=9QžÎÈÁÆi'..4>IVfs”˜››’‹‰¹ÄÃÉ—/*'&(&'$"!!6›š™¡a!9q•›œž £¥§«®²µ·¹»º¹¸µ°«¨¦£¡ŸŸ›››œœœ›žŸ¡£¤¥¥¥¦¥¦©§¥¥¦¥¤£¤ Ÿœ˜ƒ™©¨©ª¬«ª«ªªª©©«««ª¬°³³²¯«©¦£ŸŸ¢¥¦¨©©««•‹‡ƒ|xxz{}~~ytpopppqrsttvwwx{}}~‚ˆ††‡ˆ‰€vfVO[n‡’–—™–‘ŽŽ””••”–šžžŸŸž ¡¢¢¢£¦¨ª°²¶¸¸¹º¼¾ÁÂÄÅÅÆÇÆÆÈËÏÑÑÐÑÏÏÏÏÏÎÍÐw:DA?@ÎÊÊÊÉÌÒ¯c.3<=?:h¸ÑÉÄÀÄc243310.--/1/1552215¨ÇÂɈ140.,*(''&#""!! 9˜™–›Z " EŽª£§©«®¯±²³·¸¸¹¸·µ°¨¥£¡¡Ÿš™šš›œš›œœŸ¡¤¤¥§¨¨§§¦¥¥£££¤¦¥¥¥¡Ÿš•€’¦¥§©ª«ª¨¨§¨¦¤¦§§¦¦¥¨ª¬ª¨©¨£žœœŸ£¦¨©ªªªš—†€~|ywxyy{}{uqmlmmmnooppopqsrsvvrutqqqrrnnj_UKTl‡””•––’ŽŽ‘’’’”——™™› ¡£¥¥¦¨©¬¯³µ·º¼¿ÁÅÅÄÅÆÈÉÊÍÐÑÑÏÐÎÎÏÎÎÌËËk;B@=C´ÌÈÊËÑË„80:=>9B…ÈÑÇÆÃÀÆ^43225689:9877778:<@®ÄÂÊ~.1/+*)'%$#!"! "! @™˜™N!#$%-†¯ª°±³µ´µ¶¹¹¹¸¶²©¦¢¡¡žœšš›š››™—˜™œ ¢¥§¨©¨§§§§¦¦¤¢£¤¥¤¤¤ ™˜¡¤¦§©©¦¡ ž™––™ ¡¢£¢£¤¦¨§§§§¨¦¡œ™šž¡£§¨¨©©‘“‘ˆ€|zzwuuvxy{xsmjiiiiikjllijjjijljhggfeeecb]WMIMe{ˆ˜•““‘‘ŽŽ‘Ž‹’••–™šœž ¢¢¤¢¢¥¨ª°²´·º¼¿ÁÂÃÅÆÈÉÌÐÐÑÑÏÎÌÍÎÍÌÊËÇ[DC@9I»ÊÅÅγc.4=@<9V£ÐÍÆÇÇÃÁÃU.123242024467:9797=¯ÀÁÉs.1-*%*71/.,)(#" HŸ™”’C(%#"!%$&($.”³°³´µ·¶¸¹º»»¹µ°«§¥£¢¡ž›š››œœœ›˜˜™ ¢¤§¨©©§§¦¤¤¥£¢££¢¢¢¢Ÿ›š—’™¡¤¥¦¥¢œ—‘‰€~€‡˜žŸ¢£¡¡¦©©¨¥ ›˜˜›¢¥¤¤¦¦§_ˆ‡~zyxurstwwxtlifeecccedbcc`b``ac``_^^\ZYXTPF@I_x„˜—”’‘”’Ž‹ŽŒ’’’”•™›ž ¢¡ ¡¤¦ª¬®²µ¶¶¹¾ÀÃÄÄÆÇÊÏÐÑÐÎÍÌÌÌÌÉÈʽL<@@8SÀÃÃÈ”D3::;:;t½ÎÇÆÅÅÄÁÀ½K034.B€yl\TJ:788669Z·½¾Åi10++&v “•“Œ…{tmffdx•“ŽŽ@(x†‚~vY3"$(*-&A«·¶·¸¸¹»»ºº¹¶±©¨¥¤£¡ žœœœžžžœ›šœŸ¢£¦¨ª«ª¨¦¥¤£¢¡¡¤£¢¡ Ÿœš—”“𡣦¤Ÿ™‡ƒyuuu‡’™›œ ¤££¨«¬«¨£Ÿš–™Ÿ¡¡ £¤£¢4^†……‚}{zwtqpqsutmifa`^^][XY[WVTWWWXYXYWUSQOMID;34]w‹“——”’’‘ŽŒŽŒŠŒŒŒ‘’’•–—™Ÿ¡¡Ÿ ž ¡¡£§ª®±³µ¸»¼¿ÃÅÅÄÇÌÎÏÎÍÌËËÊÈÇÆÈ³@48:6U½Â¾s24<881DËÇÀÀÂÁÁÀÀÀ·B256/_ËÇÉÄÀ·¦—”˜©¸¾½¿Äb)-+++‘²©§§¥¢¡Ÿ¡¡žžœ˜“‹47 ¡¤§«®›Q&*,,.'{¾¹»º¸º¼»»¹·²°«¥¦¤¥£¡¡žžž ŸŸŸœœž ¤¥§©ª«ª¨§¦§¦£¡¡£ ¡ œœš–”•› ¥¦£Ÿ•Šytrtv}†˜œ¡¦©ª¬±²±®ª¦£Ÿ¢¥¤£¢¢¢¡ž@5`†ƒ|{vwurooqrtqifc`^\XVURQPMNOOPRRPPOLKID@7,!T}‚ˆ•–”“‘’’‘‰‡‡Š‘“”–˜—šœœžžžž ¤¨«°µ·º¼¾ÂÄÆÆÈÊÍÎÌËÊÈÇÅÃÂÂÄ©93540O¾°V-68760S¤ÆÁ¾½½¾¾¾¿Àó?3451jÈ¿ÀÂÃÃÄÅÌÎÉÆÃÁ¿À¿Â_,.,()’«¤¡Ÿž›š›˜˜˜—•“,= ¡£¥§³«P(..0,O¶»»»»½¼ºº¸µ²¯«¥¤££¡ ¡œžŸ ŸœœœŸ¤¨¦¦§©ª©§¨¦¦¤¡ ¡ Ÿžœ›™˜–“”›¡¥£š“‡{sqnnpuz‡’›¡¥¨ª®°±¯®ª§¤Ÿ¡¢£¥§§¥¥£ŸD@<^ƒ~~{xwutppprrqoida][YVSPPMHFECDIHHGEB@:0!P|„‡Ž“•–•“”––”‘ŽŒŠ‡†ˆŽ‘“–––˜™™™˜™š››œž ¤¨«®´·¹¿ÃÅÅÆÈÌÌËËÆÃÀ½ºº¹¸»™-./.(LŽA&53350d²Ãº¼¼»»¼¼¼¾¾Á¬>4352nÆ¿ÀÀ¿ÀÀÃÄÃÂÀ¿À¿¿½½X++)&*‘¦¡Ÿœ›————˜š™”’†'F¥Ÿ£¥¨©«¸œ6,/12<¤À¼½¼½¾¾¼¹¶³®©¥£¢ ŸŸžžžžŸŸŸŸŸ›œ¢¨¨¨¨©ª¨¨§¦¦¥¢ Ÿ ¡Ÿœš—•–”’”šš‘ƒ{qnnlmmptx{ƒŽ˜ ¡£§ª««ª¥£¢ žœžŸ¢¥¤¤£¢¢DCA8f||{zutsqnnnmnnjf`][XVRQMKHD>;99<=:3+'
Hy‚†Š“”””•””“‘ŽŽ‹‰ˆ‡ˆ‘’‘’”“’”––——˜™š›œž¤§ª®°¶¼¿ÀÂÆÈÌÌÉÆ¿¸²®®®¯°µŽ%)'()+."-.-,1l·º´¶¸¸¹»¼½½½½À§84493qĽ¿ÁÀ¿¾¿ÁÁÂÀ¿¿¾½»ºQ%'%#'ŠŸ›™šœ›–•—™˜˜•‘Ž€#M§¢§©«¬®²·Q*//34“Á¼¾¾¿¿À¾¹¶°ª¦¤£¢ žžŸ Ÿž ¥¨§©ªªªª¨¥¥¤¢¢ŸŸ Ÿœš™˜•“‘”—“…}tllmjmmptsux‰“™™œ £¢¢›‘‘—œ›™š› ¢¢¢¢¡BCA;=jxwyvtrromllljjhb]ZXUSPMJGB;7542(
D|ƒ„ˆ“•–—•’‘ŽŒŠ‹Œ‹ŠŠ‹ŽŽŒŽŽŽ’““““•—˜™˜œž¢¤§ª±¶¸»ÀÃÅÉÊÅ¿µ¥¡¡¡£¦¯€!&%%%$%))++.y¹¸±²³¶¸¸¸»½»¹ºÀŸ53242tȾ»¼ºº»¾½¼¾¾¼»»º¸ºM%&#"%‡˜–•—–•––“’’”Œ‹Žw\ª¤©®®²´»_(1263–ýÀÀ¿¼¹·´±¬§¤£¢¡¡ŸžŸ ¢¢¡ Ÿœœž ¤§¨§©«©©¨¦¤¤£¡žž Ÿœ›™˜—•“‘ƒurkijkloqqtstx}†Ž“˜™š˜ƒ||ˆ•˜––˜šž¡¡¢¡¡CCC@7?kuvxurqnlkkjjlhc^ZWTRPMIE@<851(
=|‚……ˆŽ”•–•”‘Œ‹Š‡‰Ž‹‰‰ŠŠ‰‰ˆ‰Œ’’‘””“’–™œ £¨®±¶¹¾ÁÄý¶ª¡œ˜˜™›Ÿªp!!!!"$%&&%T¶¯¯°²³µ···¸¶¶¶½‘/100.R˜¤®·º¼ÀÀ¿½¼»¼¹¸··¶H"$ #‰¢›œ™–’‘”•’‘‘ŠŠŒm e¯§ª¯°±¶¹¿Y/0588¤ÅÁÀ¾½·´²°«¦¡¡¡¢¡Ÿ ¡¡¢£¢¡ žŸ¢¦§¨§©©§¦¤£££¢Ÿœž ž››š˜–”’މ„tolkiiklnqrsusx~ƒ‰ŒŽ’‘‹€tpqŽ““•–šœžžœ›œCBCA=8>outtrqmmlijhiheaZURRQMID>975(
8x}ƒ„†ŠŠ”•“‘ŽŠŠŠŠ‰ŒŒ‹‰‡†…„„ƒ†‰‹‹ŽŽŽŽ’–›¡¦©±³¶¹´±© ›—•••˜›¤h# ""$$3›®©®¯¯¯¯¯°³´´²»„*--,,'(1>Lau…˜Ÿ¡ §¼¸´±E#!FTSYYZ`dagigkpŠ–c " p±«°³¶¶·¼¬A6346F·ÄÁ¾¼º¶´³®¨¢¡ŸŸŸžžŸ¡¡¢¢¡¢ Ÿ ¤¤¦¦¥¦§§¦¥¢¢£¢¢ š››˜•”“‹€{vqnkihhkmnqrqtv{‚„‡‰Œ…wpor|‹’’•–™š›š–““AA@@>=5Bqrrtqnmkifffffc]WSROMHB=74*
0r|‚ƒ…ˆˆ’”ŽŒ‹‰‰……‰‹Š‹Š†ƒ€€€ƒ„…†ˆ‰‡ŠŠ‰ˆ‰ˆŠ–˜œŸ¢¦¨««©¥™–”‘”––—ž`5 !";ž§¤¦§¦¥§¨©©®¬µt$,+)*+(('&$'/028:;5„Áµ±«? @“’“›_ "&"z´¯´·¸¸»Ãl59573tÄ¿¿½»¹¶µ²¦¡Ÿ ž›œŸ ¡¢¡ ¡¢¤¤¥¥¥¥¥¥¤¤¢¢¡ žš˜˜š˜“‘Ž‹|trqmlighmlnpqruwz}€ƒ‡ˆytomrx~ˆ‘”˜š›™—‘Šˆ@@@?<=;1Jpprqnmkifcacca`[UUPMFB<9+
3m|…„†ˆ‰Š‹ŠŒ‹Š…‚„„ƒ‚€€}}}||~~€€‚ƒ€ƒ‚€ƒ‰Ž’–˜™ £¤¤¡œ—•““–––”šQeTM£››Ÿ¡¡¢¥¦¦§§§e))(*+,+,./25446872‰À³§>Dš˜š¡^##&$µ°µ¶¸½Å~/9875=®Á½½¼»¹·´°©¤ŸžžœœœœœŸŸŸ ¢¢¡¡£¤£¢¢¤¤¤£¢ žžœœœ™˜˜–—”‘ˆzupljifehkkmooqtwz}~€}yslknv|€†‹”˜››š“Šƒ???>=:75,Nnqppmjgedaa_]^[XQNHB@8$ +
)jx€‚„†ˆŒŒ‹ŒŒŒŒŠˆ„~~|xwz||}|{z{{{z|}~{z{z{|~€ƒˆ‘“–™Ÿ Ÿ™—“‘“–˜•–Io˜2j¡–šœ™ ¢¡£¢£U%%'$)'%'+,.0214785޼°¨£8 GŸž¨\#&%(&‡Àº¿À¿ªj/7;:82ŠÇ½¾½»¹¶³®©¤Ÿœ›››››œ›œŸžž ¡¢£££¡ ¡¢¤¤£¡ Ÿœ›žŸš••••“‘ŽŽŽ…ytpmhfeeeilmnpswz~}}|zvvnikrx~~‚Š•›ž›–Ž…w?>>=<:85/)Kpllkihfcb_][ZXUOIE@.
+
%ct{ƒ…†‰‹‹Š‹‹‡…‚€|{zxuwxzywyxvwxwwxxuttuwx||{}€ƒ‡Œ“—›œ—–’Ž‹Œ”–‘Cq™~ x™’”–š››œžŸ¦F!"#)~ŠveTJB:965767’·©£›0K£ ¤¨V$&&)#^ˆƒzs[:+49=<1xƽ¼½»·³°¬¦¢Ÿœšš™š›š›œžŸžŸ¡ŸŸ¡¢ žž¡¢¡ ŸœŸžœš—‘‘“‘Ž‹ŒŠ‚vqmlifdcehkmnsy}yxvvtpghmrx||€Š•šœ•Š‚{w?==;;8641-*Gjkgffdcc_]YVRLLE,
+ +
%_ryƒ„ˆˆŠ‹‰Šˆ‰ˆ„ƒ‚}{zyxvwxwvuvvttttttqorsstwwwww{~…‰•—›š—”ŒŒ’–‘:m‹‘g&„Œ’”•–˜˜™›œ7!! 2¦¸¹»º¸²«¦¢œ™––¨¬¡š+*UTTQONKIHIIKJrª¤¤§N$''))&))%(*/34:6:}Á¿¾¼¼¸³¯«¦¢ž›šš™™š™››œžžž žžŸŸ ¢¢ Ÿžžœ›œ™–”’’‘ŒŒŠ‹‰spmkidcdfiilqz‚…ƒ}wuusrqkchoqv|€†”›ž—’†xw><;;:54411/):_gfb`ab_ZUMKI:!
+
Wrw}‚……‚†‡„‡ˆˆˆ†„€|yzzvwvuutvtssrsrqompqptutwwvwy€‡ŒŽ–›™•“Ž‘Œ6n‡…‰J6Œ’“”—˜—‘.7Ÿ¦«²µºº¼¾½¿¿¾º±¥š“‚%C”‘‘”••—œ£¨§¥¤ªL$((*,-020234213PŸÊÀ¿¾½º³°¬¨¤ œš˜™˜™™™™šš›š›ššššœ›œž ¢¡¡ Ÿž››››š™–•“‘ŽŠ‹‹Š‰‡~vtpnhcbegklqw„ƒxvtqonlhdhnow‡’ ž™ˆƒ{ww><::85434311,1Oa^\\ZVPNL7
+ +
Lqx|‚ƒ‚‚‚„…ˆŠˆ‡‡‚{{ywwvtuutttrqsrrpooqprrqssttx|†‡–—–“‘‡‡†ˆ‰‰6o~|}‚4G‹ˆŠŽ‘’’”‡%:˜š¢§¬°²³³³´²°¬¥šŒw J‹ˆ‰Œ‘’–—™œ¡§¨ª©¬M&++-/-+.,-1/?b‘ÁÌÂÁ¿»¸±®¬¨¤¡žš˜—˜—–“’“””’‘••”’’–—–™¢¡Ÿš˜—˜˜–“““’‘‹ˆˆˆ‰ˆˆ„}|ysnidbdhmu{ƒˆ„€yuuspolhdefimw…“›¡¡Ÿ™”ƒwuw<;9877655331/.)?ORRQLIA"
+
<t{}‚ƒƒƒƒƒ†‰‰ˆˆŠ„‚~~{yvussssssqqrpqqpnqpqpopqrtwz}„‰‘••’‹†„„††‡5"w€{{|t#b‹„†Š‹Ž|<”’”•𠤣££¡Ÿš“Žˆ†jT‰†Š”–˜œ ¥¨ª¬¯±M)/164>LVas°ÂÉÅÁÁ¿¹·´¯¬©¥ œš•“‘‘ŒŠˆˆŠ‰ŒŽŽ‘Ž‘•–˜˜••–“’‘”“Ž‘’Œˆ‡‡††‡ƒmtxslfcaensy†ˆ†{tpqonkecfghio‚“¡Ÿ›•ˆ‚|xwz;::877442320.--3?A@EG5
+
*p{~ƒ„„„……†††††ˆˆ…~€€}{wvtqrrqrnqqqpnmnoppponpqruxz~†Ž’“’‹ˆ„‚„„/"uƒ‚~{€fp‡ƒ……ƒ‡t>‹ˆ‰‰‹ŽŽ‘’Šˆ„€€…a]‡†‹ŒŽ•–™Ÿ¥«¯±³¶Q-16;<ŸÁÄÉÌÍÏËÅÃÿ¼¶²¯«¨¢™–’Ž‹Šˆ‡ˆ†…„ƒ„„ƒ‚‚…†„ƒƒ„†ŠŒ‹Š‹Œ‹‹‹‹‰†„„ƒƒ„‚aanmjeabhmu|‚„…‚{tqoomlhb`cgiiju„”š—”Žˆ„‚zwz€97788842310/,0;A@;8=4
+
$c{€‚„„„…‡††‰‰†…‡…€€~}{xwurqsspnoooonnmnoooppqruuwz}‡Ž’’’Œ„})!wƒƒ€{Y(ƒ~€€~‚jDƒ~€ƒ‚ƒ‚‚€~}~‚Ueƒ†ŒŒ“–—ž¢¨±³µ´µQ2369H½ÍÉÊÉÇÇÇÆÄÿº·²®ª¦¡œ—‘Š…„€€€}}|}}}|zzzz{z|}|ƒ‚ƒ…†ˆŠ‰ŒŠ‡…†Š‰‡ƒ‚‚€~€‚€bYdhecabgkxƒ„|upnnmmjd__cfhhjmr|‡‹‹ˆ‚€}ww‚5676665332/,1BGDA:90
+
_}‚„……„‡ˆˆ‹ŒŒ‹‡†„‚€~}|{zwutttpopnoopqpprrsuvxwxy{{}‚‰’’‘‡v%&w€~~{y‚G=…~||z~_H…|}€}{||yxxz||}„Ks‹‹‘”˜›ž¥§«°´¸¹¸´J1344J¹ÆÇÈÉÈÈÈÆÃ¿»¶¯ª§¢œ˜”ˆ…ƒ~|zzzxwvxywvwvuuvtvyz{y{~~„…ˆ‰†ƒ‚ƒ‚~}}~~}`[`fccbcglw€ƒ‚|wsoonlke_^`ceggjkmsz‚…ƒzxz}‚‡578645542219EIGB>?:
+ +
Ly‚ƒƒ„„†…†‡‹‹‡……ƒ€~|{xvuusurqqqpqrqrtuy{~ƒƒƒ‚‚ƒ€„‹ŽŽŽŽ‰‚}{m )z}{{|}}~~4Mƒzzx}S.RV[bdhopsvv{~|€~„E"ƒ›—–›ž¢¤¨«±´¹¸¹¹»´I5693M¿ÆÈÊÉÈÇÆÃ¿º´¯§ š–Šˆ…€}|zxwvwvttutvtrrsrsqppqsuvxyyz|~€‚‚‚~€~~}zzyz}{`[`baabehox€~|xsnmlkhb^]beggeffimx€€}yw|ƒŠ7865533414BJIGE@<@
+
:x‚~}€€€ƒ„„……ƒƒ€}{zwxwuvuurrrqrru{€…‰Ž”“’“Ž‘‹‰‹‹„}yui"({zzy{|||v&^ƒxv{M#(+06:R~„=`twz†‰–˜š›žŸ±»¼À·H7776XÆÊËÉÈÆÃÀ½¸²¦ —’Œ…‚|{yvuttrrrqsttrqqpqooooooqrsuvuvxyx||||||~~|{zvvvvz|x`[___`bcis|€ztnmjigc^\`dfeggbglp{}}{vyˆ•77534322>JPJIFD>2
+ +
.wƒ~zyz|}}€ƒ…„ƒ„†„ƒ}}}||yxwwvrrtvz†‘—𠢤¤¥¥¢žš—”Œˆ†ˆŠ…|wro_PJE>Gwxzy{yzxx|dl{rwF/€€5!$&)+.1147›ÂÀõF7887hÌÈÈÇÅý¹´±¬¥ž—”Žˆ€|ywwwtrsrsrqprrqponllmmopnopprsrrstuuwyxxxyyzxwvtuvuwzv]Z[]_`delu}‚‚|tojkhie]Z\bdgffhfms{~|{{vv|‡™Ÿ7553229JVTPJHE:/!
+
*f€|xxwz||~}~‚‚„†„…††…ƒ‚ƒ€{zyzyvy|~†Ž– §ª¬¯³±²µ´±¬©¢™’‰„ƒ…†…€ytoqsx{{wvwx{{|}|{z€V#&pvyB5€0"""%'(*+,//15ŸÆÁÇ®B:;<7rÍÈÆÃ¿½·²¬¦Ÿ™”Œ†€}zwvutssrrqrrqqpomlkjiklmmnmmooppppprrsttvusstuvuuututuwoZYZ^aceinv}€|vqniigfa[[aeejhhimu|€|}|zvz†”œ¢¨52123ASWTSKE<70,!
+ + +
"[y~zwtrswwyz|€€„††‡†ˆˆ……ƒ€{{{yz‚‰‘™¤±³¸¹»»»º¹¶´±¨œ„€‚„ƒ‚|wtrqprtsstuyz|}~}}|sjbXOI;[ww@A‚~ƒƒ. ""#&')*-/2359©ÇÂÇŸ76530…ÌÅľ»¶±¬¥ž™Ž†‚|yvutttrrrqqqppoomkjihhhghijijlmmmnopoqrqppqssrsrttstrsssukVW]`efimsy€yrnlgghe\Y\aefjkjnt|„…~{zvx‚‘œ ¤§31/-3AA?A=75320/#
+ + + +U{{wrrqrrstyz}€ƒ„…ˆ‰ˆŠŠ‰‡‰‡…‚~~‚…‹“š¥³º»¼¼½¾½º¸µ±ª£˜Š~{|~€€}zyyvuttuuuvz|||~}|}}}|x{}|{xxoaZSKD<62,)$#I‚€†) "!#%')*+-33/<ÃÂÆ›S\bv‹µÇþº³®§¤ž•†~zvuttsrqppqppoommljhhghhfdghfggijllmmnnmnpqppqqrrpqrpqqrpqtqbadfhjntyƒ}wojigge^XY]cfjjlpsyƒ‰†}yut{‡“œ £¤1.,./.,.43334422'
+ + + + +
Lyxwtqpqrssty|„‡‡‡ŠŠ‰††††‡‰†„‚ˆ‹’›£«²·¹»»»¼½¼¹¶²«¢—Œ~{zz}ƒ‚‚~{zzzzy{zz|{{€‚|zzz{}|z€{zyzywxxwspnlhcYTQl…‰ƒ?2.2300.1656;@FGNSZv¾ÂÅÆÃÂÊÏÒÐÉþ¹´®ª —ކ‚|xvsrrqopooopoollklljjijmlifiigeffiklllmmnlnpomnopomoqponnnprqighjiovƒƒxqlihge`YX]cfijjmsw~…†€zvqs‹”›¡££.,+-.-/043566753
+ + + + + + + +
Euttsrsssstux{‚†‡‡ˆˆ†‡ˆˆ…†ˆ‡………‹Ž— ©±¶¹»¼¼¾¾¼¹´°¨š‚zxyz}…………‚€~€~ƒ‚„„„‡…‡†‚‚‚€ƒƒ„„€|{xyz||~}~€‚‚ƒˆŽŽ‹ŠŒŽ‘““•™ž¡¤§¦©¬±¹¼¾ÁÆÍÊÈÉÉËËÈÉÇž·²ª£›‘‰„~yvtsrponmmmnonoomklnnnoorvurpmjihhfegjjilloonoononnmmlopnmlmmqpkijkoxˆ†{tnieeb`XV[_dghkmqw€‡ˆ€zwurx‚Œ”™ £¦,+,./00224677742
+ + + + + + +
:wwvvuvttuvwxy~ƒ„ˆŠ†…‡‡ˆ‡‡‰Šˆ‰Š‹•œ¢¬²µ·º¹º»º¹³¤˜‘yutux{}ƒƒ†‡……‡…ƒ„„„„„ˆ‹ŠŠ‹Š‹Œ‰‹‹‰‰‰‹‹ˆƒ~ƒ‡†‡‰ŠŒŒŠ‘“‘“—˜œŸ£¤§¬²¹º¹½ÁÁÃÆÅÇÊËÉÊÊÊÉÈÉÇž·°«¥šˆ‚~{xtqrrponnlmnnnmoomnopprsux{yxvrqonkigfgigiklnnonnolllkkklllkmnpoljkqz‚„‡…zslhfccaZTW^dfhikqv{„‰…zvssu}‰‘˜œ ¤¥+++-/00344455762
+ + + + + + +1s{}}}|zxvvwwvzƒ†‰‹ŠŠˆˆˆ‡ˆˆ‰‹‹‘•™ ¨¬±³¶¸¸¸¸µ°¨ž˜ˆ~usqqqty|€‚†ˆ‰‹ŒŒ‹ŒŒŒŽ’““”“‘‘’“•“’“’Љˆ‡‹ŽŽ’”•““•˜——™˜˜¡¤¦¨©°°·»¼¾ÁÅÅÇÇÈÊËËÊÊÉÈÇÈÇžº´ª¢›’‰~zwutrqpqpnnnnonooopppqqsstwxz|{{zywvutppjhhhjklmmmlmnljkkjjjjkijnrplov{„‚|rjgedb_]USX^cegimsx€‡Šƒxurrx‚Œ“šž ¡¢)**,-./11214112*
+ + + + + + + ++n€„‡‡…„€|zzzzy~…‰‹ŠŠŒŒ‰Š‹‹ŠŒŽ’—𣫮°²µ··´«¡œ–‹ƒ}vurqqsuw|€ƒ‡‘””••••–˜š›œœž¡Ÿ››žš™™˜•’Ž‘”““•—–™šœŸŸœœŸ§«¯±´¹¼½¿¿¿ÁÃÆÇÇÈÊËÊÊÊÊÈÈÈȼ¸´¬¡™‹‚~yywuspnonnnmnnnoprrrrsttuuxyz{}}}}}}{|{zxunkjkkmnkllmmlljlkjihhhikmqnov…‡…~yrleddb`\VTV\bdfilot|„Š…}wsot|‡•—œ¡&''(())(''''%$#
+ + + + + + + +"k‹‘Œ†ƒ|~}}„‡Š‰‡ŒŽŒŽ‘•™¤ª¬®¯±´²£šˆ€|yustsqsx}…‹’—™¡£¤¤¢¡žžŸ¡¤¥¦¨©ª©¨§§§¦¥£¡¡Ÿš–•—ššœ››œœŸ ¡¢¥¨¦¥¥§°³µ¸¹¼¿ÀÃÄÄÅÆÇÈÉÊÊÊËÊÉÈÈÇÇÇý¸°¦•Œƒ}zxvvspnmmmmlmnpqqstrrstvwwyz{||}}€€}||}}|{xqmlkmmklkllmlkjjjiighfghiojju€ƒ{qjd^``^_ZRVZadefkkox~„…}xvqqy€‹‘“–™œŸ!! # ! !!"!
+ + + + + + + +
]˜›š˜•‘Œ‡„„‚~}‚…ˆ‰ˆ‹Ž‘’‘’”–™ ¦ªª¬¬°±¬¢™…|{xusutsyˆ‘œ¦¬±¯°®°´µ·³¯«©ª¬¬®±²¯¯®¬¬ª¨¦¡œŸ¤¦¦¤¡ŸŸ¢¦¦§§©¬®°²³±´·¸»¾¾ÁÄÅÆÇÉÊËÌÌÍÌÌÌËÉÉÈÉÇÆÀ¹´®¦š’ˆ‚{yxvtrpnllkjlnppqrsuvuwvwxz{{{|}~}~~}~}}}||ytqnnljkljljkjjjjhigfggfgjjYU_ijhfg`a^\Z]]ZTRX\`ccdgmpw}€|xtrorz…Ž“•˜™žŸ"""" !!##"#"!
+ + + + + + + +Nˆ—Ÿ¡Ÿžš—‘Љ†ƒ†ˆŠ‹ŒŽ‘’’“”–™œ¢§ªª©ª«§›’‰‚€~xwvyz~‡˜¥«£‡mWMJJP_œµÂ½³³³³³´µ³´µ´´±¯«¥¤¦«®¯«¨¦¦§«®°°²²µ¹¼¾¾¼½½¾ÁÂÅÅÇÈÊËÌÍÍÎÎÌÌÌÊÊÉÈÇþµ¯¦ š‘‹|xvutrqonlloqqssrqstvwz{{{|}~~}~~€~~}~}|||xurppnlllkkjjjihihffdeeeicMJKSSTWYXY[[[[ZUQTX]bdeehlqx~}xurnpv€‰–˜˜š›œž !!"""!"
+ + + + +<€‘Ÿ¦¥¥£ž™”І„ƒ…Š‹ŽŽŒ’““–™š¢¥§¦¦§¨ª©¥™‹‡†‡†ƒ…ˆŠ‹‘š¨±–c<0034453048Gw¯Ä»¹ºººº¸¸¸¹º·µ³±°²¸º»¸³±¯¯²´³´·¸¸¹¼¼¼½¾ÀÁÀÁÂÄÆÆÇÉËÌÌÍÍÌÍÍÌËÊÇÇÅÁ¼³§ ™Š{xursqpponokWZ]aitvuxxxz|~~}~~~€€‚€~~}|||zyxusronnlklljifihfbcbbcf[FIJJMPQRVY[ZZZVQRV[^_cfeimqy}ytrpou~…Š“™™—š›ž !!"
+
+ + + + + + + + +
)q”Ÿ¥©©©¥¡›–’‹Š†ƒˆ‹‘Ž’••—››ž¡¡¢¡ £¦§¨¥¡œšœ›š›Ÿ£¤§«·§f0(/7;889:9<::5.@{¼Ã»¼¼¼¼»¼½¼º·¶¶¸¼ÃÆÊÆÅþ¸¹ºº»½¼¼¾¿ÀÀÀÂÄÄÄÄÆÅÆÉÌËÌÍÌÌËÉÊÉÉÈÆÄÀ¼¹°©¢”‡ƒzwutqrqpppoy>j{xxz|{|~~~~‚ƒ„…„…„ƒ‚~}}}||||zywvtrollkkjhghgeaababgM=BFIKNOQVYXYXWQOQVZ^`bdgkotyyuopqot…Š’–•˜šŸ
+ + + + + + + + + + + +`Š—¢©«ªª©¤žš–‘Ž‹ˆŠ’“’“‘“–—™šœŸžŸŸžŸ¢§¨ª®²·¶²²²´·¹¼ÄšB'1524:95457;96784.Q©ÉÁÂÀÁÂÁ¾»ºº»¾ÄÇÈÑ—q‚Ž™¶½»»¼¾¾¿ÁÂÃÃÃÄÆÆÆËÐÐÏÎÉÊÉËËÊÈÉÊÇÅÄÁ¾¹´£§c&*),+Mxstrqppont[f|ywxz{|}~€€„†‰‹‹‹‹‰ˆ…„‚}{{||}~~{zzxusrqnllkjhgfccaaac=3<BGIKMOQTWWWSOOSY[_bbdgkptxvpqqosx…ˆŒ’’”•™
+ + + + + + + + +
Gƒ’Ÿ§««¬¬ª¦¢˜“‘ŽŽ”–•–——˜™š›œœš›œ››››Ÿ¦°¶¼ÂÄÂÀÀÂÃÅÆË‹5172692.5=?;963367583A¡ÍÂÃÄÃÂÀ½¼¾ÃÆËÌÒ¯:-343‚ƺº»¾ÁÂÁÁÂÅÆÆÆÅ˨ynad¼ÊÊÉÉÉÇÆÄÁ»º¶°© ››D6trsrqqpnpp'f~yyyz|}~ƒ†‹Œ’ŽŒ‰‡‡„~|{}~~~||{xuuusqnnlkkkheb`cQ248<BFIKMORTUSNMPTX]^_`deioswqppons|„„ˆ‘”˜ž
+ + + + + + + + + + +1t›¤©«¬¬¬«¦¢Ÿœš˜”“–—˜››™š››œ›š˜––—˜™ ¬¸¿ÄÆÅÆÈÊËÌÎ|)1:7723Mw³¦c=0:98=;>œÉÂÃÃÃÃÂÄÇËÌÌÌÆV16697P»¿¾¼¾ÀÂÃÂÃÄÇÇÇÇÏo0240KÈËÉÉÈÆÄÃÁ¾¹¶°¨ š“‰//qrqqqqpoyHi€|||}~€‚‡Œ”••”“‹ˆ†„~€€€}~}zxxwvurrrpopmlhdd>.379>DFJLNQSSPLOSUZ^^]_dghnsspnoprv|‚‡ˆ‹ŒŽ‘”˜
+ + + + + + + + + + + + + `‘Ÿ¦¬°¯¯®«¨¥¤¡›˜™˜˜›žžœ››™—•’‘‘‘“˜¢°ºÁÅÆÈËÍÍÏz-58::0N•ÁËÊÈÈÉÊÌǯf59;;:.PÀÆÃÂÁÂÅÉÊÌËÄÇ{+633576œÅ¾¿¿ÂÃÄÃÆÅÇÇÉÊË\49:4eÍÇÇÇÅ¿¼¹¹¶§ ™‹t!-oqqrrsqsck€€‚„†‹‘”˜™˜—•“‘Œ‰……„‚ƒ‚€€€€~|{{zyxuxxvwuturp]/+18;;ADGJMPRQMKQTZ]]^]`dginropootv{|€‚ƒ…„†ŠŒ’–
+ + + + + + + + + + + + + + +
R€Žœ¤¬²µµ´±««©£Ÿœœœœœž žžœ›•’ŽŽ•¨´»ÀÃÇÊÉÑŠ-4696/sÁÑÉÅÅÅÅÅÆÄÁÃÃ…79<1MœÃÄÃÁÂÅÊÌÎËÂÆ¢//10114.kÅ»½ÀÂÄÄÅÇÇÇÈÉÍÁH6885~ÌÄÅÄÀ½º·²§Ÿ™”‡ƒ`,npqqrsrw1n‚‚ƒ„†ˆ‰Œ“–™™™˜—•’ŽŒ‰‡ˆˆ†††††‚‚‚‚€|~|}|{{{|}|{zyws3%+-3:;?BEHMQRPJJPUY]^_`acdinmlmoswz„…„‡†‰Š‹Œ“
+ + + + + + + + +
=yˆ˜¡ª±´¶·¶³®¬«¦¢¡¢ ¡¢¡ Ÿž™“ŽŒŒ‰‹Œ”žª³¸¼¾Àͱ707<71ƒÎÎÇÈÈÈÆÆÅÆÆÅ¾ȉ32a¸ËÃÄÅÆÉËÌÌÉÀ¼¹H&0/0001/?³½¾ÁÂÄÆÆÇÇÇÇÇË«:9874‘ËÁÁ¾º¸³¯ª£œ˜‘Š‚B#kpprspyRtˆ†ˆˆ‰ŒŽ‘•˜™œ›™—•‘ŽŒ‰Š‹ŠŠ‹Š‰‡…„‚~~~~€€}||U#*,7;?ADFKPOKIJOUY\]_``ceiljklov~ƒˆŒŽ‹ŒŽŒŽ‘’
+ + + + + + + + + + + +
'j‚Ž™¡¨¯²´³°¯®®ª¥£¥¥¥¥¤¢¡ žœ—“‹ˆ‡…†‡ŠŒ’©¯²´¶Äs+:<=2ƒÒÇÅÆÅÅÈÈÇÇÇÆÅÅÃÃÉ„ÆÌÅÆÇÇËÍÌÊÆ¾´¼n!--../1241†ÆÀÁÂÄÅÆÅÆÇÇÇÏ”4:889ÆÀ¾½¹´®©¢”Šƒ|zy.eqrrrshv‰‹‘‘’”˜›ŸŸžœš–’ŽŽ‘ŽŒŒ‹ˆ…„‚€€‚„‚€~|r# +!(,7<?BGJLKJGJMRVYZ\]\acehijot}‡”•”“”•••–••“’
+ + + + + + + + + + + + + + + +
Rx…“ž£¦ª¬¯²²±¯«§¦§§¨§§¦£¡ ž˜‰„„ƒƒ„…†Œ’ž¤«°¶´D-6:5XÅËÇÈÈÈÈÇÆÇÇÇÅÆÆÅÃÁÇÊÇÆÆÈÉÊÌÍËÇÁ·¸”,+,./-/2286VÃÂÂÂÂÂÂÃÅÆÆÅÍz5:89?«Á»¹¸±¬¨Ÿ™ˆ…€|xycZtrrsz;#|Œ‘’’““”—™šž ¡¡žœ™—”“”••••“‹ˆ‡…‚‚‚‚‚‚……ƒƒ‚{~B
#(.8>AGJJJIHJMPUXYZ[\_abeknt~‡Œ’”“”—™˜———˜™˜˜
+ + + + + + + + + + + + + + + + +
0u›¢¦ª°°²±¯©§¥¦§§¦¥¥¥£¢¡›“І„ƒƒ„ƒ„†Š“Ÿ¨°¹¡-+052ÌÄÆÈÈÈÉÈÇÆÆÅÄÅÅÅÅÅÇÆÆÇÇÊËËÌËÅÁº¸³E(0./+::279:9¦ÅÀÁÁÁ¿ÁÄÆÅÂÆa3979E¯¼¸²§£œ’‹„ƒ~zxu|ADtsrxZ)…‘‘•––––˜™š››¢£¢ žœš™™š™™š™—•’‘ŽŠˆ‡„…ƒƒ„„†ˆ‡Šˆ‰ƒ|j"(+/9?CHIHFGHLNRUXY[\]^_ajr~…‹Ž‘”•———•”—›œžœ
+ + + + + + + + + + + + + + + +e€Œ™¢¨«¬¯°³²°®¬ª¨§§¦¥¤¤¥¥¤£¡˜ŽŠˆ……‚‚‚ƒ…Š“ž¨´‘%(),2¦ÄÁÄÆÆÇÇÈÇÆÅÄÃÃÄÅÆÆÈÇÇÉÉËËËÉÆÂ½¸¾s$/0/01˜x-;8;6oÇÀÂÁÂÂÂÅÆÄÃÃS4765F°¶±ª¤¡™•‚€|yxutn"$%prwo"*.“—šœœ››œ›œ ¢£¢¡ŸŸŸž›™—”‘‹‰‡ˆˆ‰Š‹Ž‘’‘‘Š:
&).4<AFFEDFILNPRTYZY]]\]amy‚„ˆ‹“””–”•—›š™—
+ + + + + + + + + + + +O„ˆ– ¨¬®°±²´²°¯¯®ª«©¨§¨©¨¨§¥¤ž•‹†ƒ€ƒ„Š“˜¥ "$)3¨¿ÀÃÅÇÈÈÈÈÇÇÇÅÄÄÅÆÆÉÈÈÊÊÊÉÈÆÃÁ½Ãš11114,wʪ96589C¶ÄÂÃÃÃÄÆÆÃĺF6763D®¯¨¤ž™’‡€}{xutxN#NWw|F8Y5Œ—›žŸžžžžœžžŸ¡¢£¢¢¢¢¢¡ Ÿ›™—”’‘‘ŽŽŽ’•—š›žš™“`#)-4:@EFDCEIKNPRUVXZ\\[[[`fnuz€…†‰ŽŽ“•–”•“‘ + + + +
+ + + +
+ + + + + + + + + + + + + + + +,w„“¥«¯°±²³³³²°®¬¬¬¬¬«ªª©¥Ÿ–Œˆ…‚€€‚„Š›~ "* ½»¾ÀÄÅÆÅÅÅÅÅÅÅÅÇÇÉÊÊÊÊÊÉÇÆÅÅÂñ@.4271L¼»¿`066:2‹ËÁÁÁÄÅÅÄÃİ?7640D¨£žš”Š„€}{xutvt-:h-|j}UB—•˜œž ¡¢¢ žžžŸ ¡¢££¤¥¥£¢ žœ››š—””“”“”–˜›ž¡¤¦¦¥¥¡‹, +!'+29@DCCCEIJJMPTUUYZYYYY[\^hmrx{}ƒŠŽŒ‹‹Œˆ… + + + + + + + + +
+ + + + + + + + + + + + + + + + +
\‡— ¨¬®®±³¶·µ³±¯¯®®°±¯®¬«¬©¥ ˜ˆƒ€€ƒ‡$‡º´¸¼¾ÀÁÁÂÃÃÁÁÃÄÇÈÊÊÊÈÇÇÆÅÄÂÂÂÃ]*63352›Â·¿.6685VÃÀÀÁÃÃÃÃÂÆ£7710)D£›—“ŽŠƒ|yywust`Iw3U7\“OI—–™œŸŸ¡¢£¢ ¡¡¢¡¡££¡¡£¡¡ Ÿžœœš˜—˜™š›¡¢¦©«°¯®¬¨¨i
+
$)28?CBBEEFGGJNPSSTWWWXY[[^cfiortxx{€€€€‚ + + + + + + + +
+ + + + + + + + + + + + + + + + + +
9u…‘¥«®¯±²²³´³±¯®®°±°¯®®®ª§£ —އ„~}~„Š6U±®²µ·º¼¿ÀÂÃÂÁÃÄÅÈÉÇÆÅÄÃÃÂÿÈ~'2212,sǺ»¿·B47587 ÇÀÃÄÂÂÀ¾Æ”/2-+#AŸ”މ„~zxvwvtrwEY}Y2–LR˜–šŸ¡¢£¥¥¤¤££¡¢¤£ ¡¡Ÿ ž žœœš› ¡¤§ª°²´¶¸¶¯¬9 + + + + +$(28>BBCDEFFGGIMRRSVUVWX\`_cfhlnoonnsyxy{€… + + + + + +
+ + + + + + + + + + + + + + + + + + +fŠ˜ §«®°±²°²´³²¯°°°°¯°¯¯¬©§¢œ–‘Œ†~~~~‰\¨°¶º¾ÁÂÂÃÄÅÆÆÆÅÃÄÅÄÄÃÅÃÂÈ£00113+I½À½½½Ãm08552lÇÀÃÄÃÂÀ¾Å‡+,(%!A—Іƒ~|wwxvuutsv1f|}/yš–M\ž›žŸ ¢¤¦¦¨§¥¤£¢¢£¤£¢££¡¡ žœœœž¢¥¨®±³·¸¹ºº¹¶²¬_
+ + (07>AAEGDGHFGJMPPQSTUVXY[_acedgihkmquw~„’˜ + + + + +
+
+ + + + + + + + + + + + + + + + + +Nz„“ž¥¨¬¬¬¯±²²³µ´±²²±±°²²²¯®«¨¤ž˜“‡ƒ€€~~~!F¥¡§²¶¹½ÁÃÃÄÅÄÄÃÅÂÂÄÃÃÄÅÄÈ¿N,300/7¤Å»»¼¹¾œ13223C¶ÄÃÅÃÁ¿¾Ãx%&" Eƒ|yxvuuvutttr)p‡hO£š™Hd¦ž¡¢£¤¥¦¦¦§¦¤¤¢¡¢¢¡¡¢¢ ŸžžžŸ¡¦ª¬°´¸º½½½¼»ºµ´‰# + +&/8?BBFJFEJHGILOOOPQTVXXYZ\^^acdhjnr}ˆ“œ¢¥ +
+ + + + + + + + + + + + +
It€Ž›¡¦©ª©¬¯±³µ¶´²±±°°±±°±²²¯§¡œ•Іƒ~}~†Z]¥¡§¬¬¯´»¾¿À¿¾ÂŪÂÅÂÂÂÄÄÃÊl-630.,€Î¾ºº¹¸¹½M-3251ŽÊÂÅÃÀ¾¹½l!% NŽ~|xutuuuuttui"%z‡‰A&‘£ £F ! k§¢£¤¤¥¦¨¨§©¨¦¥¤¢£¢¡¡ ŸŸŸŸž ¥ª®²µº½¿ÁÂÁÀ¿¼¸µ§J + + + + $-8?EGIKJFGIIIHLMLLPRSVXWWY]^`bfltŠ™¢£
+ +
+ + + + + + + + + + + +
Tk|‰”£§ªª¬¯±´·¹¸¶µ³²²²°¯°°°¯®¬§¢Ÿ—‹†ƒ~}}€€„6b¤¨«¯´¸¸»»ÂÂ~?vËÁÁÂÃÀÌ19632/.[…Ÿ²¼¿ÁÂÌ€,5460^È¿»¶ºa !V…}{xvuuuuvvuwc*„’œ}"d®££¨M!!"lª£¤¤¥¦¨ªªª«¬¨¦¦¤¤¢¡¢¡Ÿ Ÿ ¡£¨²µ¹¾ÀÂÂÃÂÂÁ¿»³®k
+ + + + + +,7HGFHJKIGIIIGIIIJLOOTUUVY^`cnt„—›œš™•“’“
+ +
+ + + + + + + + + + + + + +
agm€Œ—¢¨ª«®±´µµ¹¹¶¶´³³³²³³±±±°®¬§£ž•ŽŠ…‚}~€y$G•¦§®®®³ºÁ®_/83ɾÁÁű?56651--&$,>Qit{…m364459¬ÄÀ¾º´®²S_}{yyzyvwvvxzy|[/›žŸ§V 1¢®ª©«U!$o®¤¥¥¦¦¨ª«ª¬«ª¨¦¦¤¡¡¢¢ ŸŸ ¡¤ª±µ¹½ÀÂÃÅÄÃÃÂÁ¾º°‹*
+ + + +/HKFHJJKKKIKIGHJKNPSSVZ_cglsŠ‘‰‰Š‰‹‹ + +
+ + + + + + + + + + + +
$hfg{‹”Ÿ§«¬®±µ¶···¶³²³²²²²³´´´³±°¬¨¤–‹†ƒƒk.g‘§®°²l9'5997’ÈÁÄÄb287670+*+)*-*+-.0076446,tº¸¶°§§Bk{wwyyyyzzz{}†W0˜¤¥¥«ž2#$s¶®«¯] #$k®¨©¨§§¨ª¬¬ª©©¨¦¥¢Ÿ ¡¡ ¡¢¦«±¶»¿ÀÂÄÅÆÄÃÂÁÁ»²£F + + + + + + 3HFIJJKNQQNPRPRTWX^`adjnruz|~|~‚ƒ…††
+ + + + + + + + + + + + + +
'kda{•ž¦ª®°²³µ·¸¸·¶µ³³´´³³µ´³³³¯¬ª¦¢ž˜‘‹ˆ…€€}{ƒc'E\a[C+ )12482O»½Ä‡3989:3.%#&'+.232367754351D·ºµ²§ š7$xyxxzzz|~~€‚†Š“` 1¨««¸v'$=¬²±°¬¯`"%$b±ªªªª©©««¬«ª©§¥£¡Ÿ ¢¢¤¦©±µº¿ÂÂÃÄÅÅÃÃÁÀ½¶¯m + + + +6@DCCGMQWWWYYXX\_adceikmoqrtttvx|}€…Š
+ +
+ + + + + + + + + + + + + + +
,nf_o‡’›¦®±³´³³¶¸¹¸·¶µµµµµ´µ³³³²¯«©¦¤Ÿš“Ž‹†‚€{z€`!&(,03.<¿À=79:=6Kš‹`='$(./1466242256*ˆ½±ª¡™’,3yvxy{~ƒ†‡ŠŽ™k"#%3«°²´³J%$u¹²²±®³l$(%\±¬««ªª©ªª©«ª§¥¤ Ÿ ££¢¦«¯²·»¿ÂÃÅÆÅÄÃÃÂÁ¿¹´’(
+ + + + +4;;:>EGMSTWTUTVXZ]`cegfijklqswwy|†Œ
+ + +
+ + + + + + + + + +
4rj]cy‹—£ª¯±²³´´·¹¸¸···¶µ´³³´³³´³±¯¬©§¢ž˜•‘‡…‚}||‚s0 #%)+&J¤ÄºÁ_.;;<:7ż»®”vVGC?<>BD91541)I¯¦Ÿ˜‘"E}wz{|€„…ˆŠ‘–œo#%%(/–¯¯°²²¹•-<«²²³±¯µz(*)#P±®®««©©©¨§¨¦¤¢žŸ¡¢¤¦§«®³·½ÀÂÅÆÇÅÄÄÄÄÃÀ¼·¦L
+ + + + + + + + +#5435;=AEJNPQSQSVWZ^_cdddinsuw{†‡Š‘
+ + + + + + + + + + + + + + + + +9qj^`mƒ“ ¦«®¯±³µ¸¹º¹¸¸¸¸¸µ´³´³µ¶´´±¯«§¢žš—”Žˆ…~}‚}L"#&f²¿·À-68<<5}ô³³´¹¾»º·´²³¸À–151-,$‡¤—Œy%5/-/...--..(o ¦{!''((±°°³´³»n{º³´³°¯²*(("A¯±±±®«ªª¨¨©§¥ ¢¢¤¤§¬®²·¼ÀÄÅÆÇÆÅÃÄÄÄÂÀ»²u
+ + + + + + + + +%0147:;=BHLOSSQRUW[\_cfhlmtx|€„‡’–
+ +
+ + + + + + + + + + + +Csk^]f|‘𣍫®°³¶¸¸ºº···¹¹¸¶µµµµ¶·µ³²°®©£Ÿ™”ŽŠ‡‚~ƒuF<е²±¸«>388;4ZÁ¹²²²²³²¶¾ÃÇÉÊÇÆÂN*-*(LŸŠˆp hª¨ˆ*++,*†µ²²´µ´µµµµ¶µ²±°³˜0))$:©´³²±¯¬¬ª©©©¦¢¡£¤¤¥¦©¯´·¼¿ÃÅÆÆÆÆÆÄÄÄÂÀ¼¹@ + + + + + + + + + + + +
,2269:<@DIMPSTRVW[^bfimqvyƒ…ŠŽ’•“•
+ +
+ + + + + + + + + + + + + + + + +Gvm_]aoˆ•ž£§¬®±´··¸¸¶µµ¶·¶µ³´¶´µµ´´´³±¯ª¦£ž›—“މ†…‚‚…‡‰xP/ ,Gz¡«ª«®´x4;864<¨À³±®¬°¶¿ÅÅÆÅÃÀÄ‚#)($ %‡……b #j¬¬‘1.-.+~º³µ¶¶¶µ¸º¸¸µ²±°´¢6)*)5Ÿ´²³²±¯¬¬«¨¤£¤¤¦¦¦©®µº¾ÀÂÅÇÆÆÆÆÅÄÄÄÁ¾¶©w*
+ + + + + + + + +/48;>BBFLPRUWWY[`dhmty~‡ŒŒ‘†t
+ + + + + + + + + + + +Rwp`\af{™ ¤ª®³¶··¶·¸µµ¶¶µ´´´´³²³³³²°®¬©§¥¡ž›—”‘Ї‡ˆ‡ŒŠ‚ztt}£££§ª®²§¤¦ š–ž¾¸µ¬©©©«³¾ÅÈÇÆÄ¿¼¹§/$$ X…‡T $d®™5,-,-s¸³µ¶¶··¸¹¹·´²±±³¨<(++.“µ²²°°°¯¯®¬§¥¤¤¥¦§©¬´º¾ÂÅÇÈÉÉÇÅÆÅÅÄþº¶‰c'
+ + + + + + + + + + + + 6:<AEFJORVZ]]abfnruz‚…‡yj]E82
+ + + + + + + + + + + +
a{veY^`qˆ˜Ÿ¦ªª¬±µ¶¸¸¸¹¸¸¸··¶¶µ´³´´³²±¯®¬««©©§¤¢œ›—’Šˆ‡†‚ƒ‡‰ˆ‹‘“šž¢¤ª¯µ¸º¼¿Á¿¸¶²¬ª©µ¾ÇÉÇÆÂ¼¶²©®Q/…ƒ…C #]²¯¤8*(,-g¸¶·¸····¸¹¸·´³³´´M,0.)ˆ´¯®®®®°¯¬©¦¥¥¦§§©¬±¹¿ÄÅÇÈÈÇÇÅÅÅÅÄÿº¶£lg$
+ + + + + + + + /:AEILNVZ[]a`chmknkeYI?2(&&'(
+ + + + + + + + + + +
gywkZ]ae€–¤§ª«°´µ·¹»»º¸¸¸¸¶¶¶¶¶¶µ³²²±®®®®¬«©©©¥ ž™˜•‘ŽŠˆ…ƒ†‹ŒŽ”šš ¥©®®±³¸¹¹¶±ª¨¯»ÃÇÆÅÁº²«¨£¤t*& !p…ƒ< !"#""$'+//447;BDy¸´²„}„Ž’¥·····¶¶¹ºº¸·´³³´¸¥˜˜’‘¨¯¯®®®¬©¦¦¦§©ª©®±¹¿ÄÆÈÇÈÇÅÅÆÆÅÅÂÀº±df
+ + + + + + + + + + +
$*3:<CFHNNOKFD<4)+&"%$&'&&
+
+ + + + + + + + + + + + + kwwj\^adrŒ™Ÿ£¨«²´´·º»º¸¸¸¸·¶¶µ¶µ´´³³³³³²²¯¬®§¥¢Ÿœ™•‘І„€€‚ƒƒˆŽ“•𠤦¨«²·¸¸·³®«ª±¸¼»ºµ°«§¢–”†{utr}}tw{{~ƒ‡——™¡§±¸¶¶·½º¼½½»¹¹¸¸º¹¸º¼¹¶´´¶µ¶´·¸¸µ¶°¯¯®®¯«§¦¦¨ª¬®°¶¹¿ÄÇÈÈÈÇÆÆÆÈÇÅÃÁ¼±©”cjb
+ + + + + + + + + + + $$!"!# """$%$"
+ + + + + + + + + + +
"rzyo^_aci|“™ £§¬±³µµ¶¸¹ºº»»º¸·¶µ³²²³³²²²±±°®¯¯°¯®ª¦¢ž™’Žˆ‡…ƒ€€„‰Œ‘”™Ÿ¢§¬²µ´´·µ²±²±²±¯¯©¡—”‘ŽŒŠˆ†‰‹‰‚€ƒ‡‘Ž“”—™Ÿ¤¦¦§ª¯²µ¹ººººº»¼¼»»¼»¹¸¹¸¸º¸¶µ¶¶¶·¶²³²°°¯®®«©¨ª«°µº¾ÃÆÉÉÈÈÇÇÇÆÆÅÄÀ¾º¯¦šmbpa
+ + + + + + + + + + + + + !###$rbTE1
+ + + + + + + + + + + + +
%tz{r\]^acm‡–›¡¤©¯³¶··¸¹ºº¼¼¼»¹·µ´²³´³±±±²²²±°±°¯ª¨¦£¢žš•ŽŒˆ†„‚„…†ˆŒ“˜Ÿ¦¬®®¯²µ´´³²³±ª¦Ÿ™’ŒŠˆ‡ˆ‰‡„‚„†„„‡Œ‘‘’˜œ¡¤¥¦«®¯°²µº½¼¼¼¼½½½½½½½»º¹¹·´´µ´´¶µµµ³±´´²±¯«¬«««¬¬®²µ¸¼ÀÅÉËËÊÈÈÇÇÇÇÇÄ»µ®¤›zagpa + + + + + + + + + +
#%%'œ›š–“‡|dF1
+ + + + + + + + + + + + +
.y}~s^[^adg~‘šŸ£©°²´¸ºº»»º¼¼¼¼¼º¸µ²´µ´²²±±²²²²²¯®®¬ªª©§¤¢—“Ї…†‡‡‰ŒŽ’”™¡¥§«°³¶´µµ··³®¥ —•‘Ž‹ˆ‡‡‡‡……†ˆ†„ˆŠ“•—œž¢¥§¨©¬¯±²³¶¹»¼¼¼¼½½½¼»»»º¹¸¸·µ´²²±±³²²²²²²³±°¯¬«««ª¯¯°´¹½ÂÆÈËËËÊÈÈÇÈÈÇÅÃÁ»´¯¦œ€adjs]
+ + + + + + + + + + + +
"#'()•–”’“”˜™™’„j9
+ + + + + + + + + + + +
1}~~ud[]`ddoˆ˜Ÿ£¨®²´·¹º»½¼½½¼¼½»¹º¹¶³²²°¯¯±²°±±¯®¯¯¯ª¨¥¢Ÿ›–’Їˆˆ‰ŠŒŽŽ—¢¦«°²³·¹º¼¹´£–•”‘ŽŒˆˆˆŠŒ‹ˆ‹‘”˜—›Ÿ¤¦¨¨ª¯²´µ´¸º»¼¼»»¼½½»º¹·¸·µµµµµ³³±®®¯®®®®®¬®®®¬«¬®¯²´¶¸½ÀÅÉËÍËÉÊÈÈÉÊÈÇÄÁ¾¸³§œŒi_flu\
+ + + + + + + + + + +
#&()*‘”““‘’“’’“—k
+ + + + + + + + + + +3€€zd[^_bdfuŽ˜¥«¯²´¸ºº¼¿¿¾½¾½»ºº»¸´²±°¯®¯°¯°±³²°¯®®®®¬«©§¤ œ™”ŽŠ‰ˆˆ‡ŠŒ‹“˜œ ¤©¯³¸¼½À¾¼¸®«¤¡Ÿ›š——“Ž’’’‘— ¡¢¥¨¬®®¯°³´·¸º¾¿¾½¼¹»ººº¸··¶¶µ³³³³³°°¬««¬¬«ª«««®¯®¯±²³´¸»¾ÃÆÉÊËËÊÊÉÉÉÊÊÉÅ¿»¶²¬¨¡•r\cimq] + + + + + + + + + + + +!$#%%)*)’’’’“““”’‘g
+ + + + + + + + + + + + + + + +<~€{h\^`cedm‚Œ˜¡¨®²´·¹º¼½¿¾½¼¼º¹º¸¸µ³³²±°°°±²³´²¯¯®®«ªª©¥¢Ÿš–•ŒŠˆ†ˆˆ‹’–› ¥¬±·»½ÀÀ¼·³²¯©¦£ ›˜œ›››Ÿ¤¦¨¯®¯±³¶¶µµ¶¶·¸»¾½»»º¹¹¸¹·¶¶¶´µ´´´³²°®«ª«««¬¬¬¬®®¯¯°³µ¹»¾ÁÃÆÊËËÌËËÉÊÊÉÊÊÈÅÁ¾»·±¯¨¦Ÿacgknq\ + + + + + + + + + + + "#%'&&&*+)“’’’’’’’‘Ž’S
+ + + + + + + + + + + + + +
>ƒ‚{l_cccedgz‰”Ÿ¨¯²´¶¸º»¼½½¼¼¼¼¼»¹¹·µ¶µ³±±±²²³²²²°¯®®¬«©§¦£¡Ÿš—‘Їˆ†ˆˆ‰Œ•¢§¬³·»¾¿ÂÆÆÄ½»¸±°ªª¨©ª¯°²¶¸¸º»º¹º»¼¹¸··»½¼¼¼»»º·¶µµµ¶³´´µ´²±¯®®®°±°°²²´³³µ¸»ÁÃÆÉËÌÎÌËÌÊËËËËÊÈÆÃÀ»·³°©£¢˜nadgioo` + + + + + + +
$&&$&(&)*(’’‘‘’ŒG
+ + + + + + + + + + + + + + + +
A‰†~o`bcgihgo‚™¤ª²´µ·¹¹»½¾½½½¾½¼¼»¹···µ³²²±°¯¯¯±°°°±±±²±°¬ª©¦£¢ŸŸ˜•’Ž‹‡†…‡‡ŠŽ“˜¦¯¶¹½ÀÄÄÆÈÈÅÄÁÀ¼º¹¹·¹ºººº»¼¾ÀÃÁÀÀ¾¿¿¾¼ºº»½¾½¼½¼¹¹¶µ´´µµ³´´´´²±°°°±°±±±²³³µ¶¸¸¸¹¼¾ÃÇÉËÌÌÌÌËÊËËËÊÊÈÇÄÁ»¸µ²®¬ª¨¡ž€cbehlrmb" + + + + + + + + +
#&$$'&&())‘Ž‘ŽŽŽŒ‰J
+ + + + + + + + + + + + + + + +
KŒ†ƒ~saachkkkkv…’𤱴µ·¸»¾¾½¼¾¾¾½¼¹····¶µ³²²±±¯®°²²±²³²²³±¯¬ª©¨¦¥¡ž™”Œ‰ˆˆ‰‰Š“œ£«±¸»ÀÇÄÄÅÆÆÆÇÅÃÂÃÄÅÆÅÄÄÅÆÅÆÆÅÄÃÃÃÃÁ¿¾½¾¾¾¼¼¾¼»º¸··¶¶µµ¶´µ¶³°°±²²²µµ¶¶·¸¹º½ÀÂÃÅÇÊËËÌÌËÊÊÊÊËÊÉÉÆÅÿ¼¸´±ª©¨¤™Šjcfikmund# + + + + + + + + + + + + + +
"#%$%'(''()ŽŒŒ‹ŠŠ‹J
+ + + + + + + + + + + + +T‹‡†‚va^chjlmklx‡•Ÿ«°°²µ·¹½¾¼½¾¾½½»¸¶¶¶¶¶´³³³³³²±²²²²±°±±²´³³²°¯®¬ª¦¢™•Љ‹Œ‘–ž£¤OHuÆÁÂÃÄÅÅÆÎÑÐÑÊÊÉÈÈËÒÑÏÈÅÉÎÌÍÎËÉÊËÆÀ¿¾¾¿¾¾½»¹¹¸·¶¶·¶·µ³±±³³´¶¹º¹º¼½¿ÃÅÈÊËÍÍÍÌÊËËÊÉÊÊÊÉÉÇÄ¿º¶³¯¬ª¨§¥¢œŽthfiikntmf$ + + + + + + + + +
!!"$$$'&&()(ŽŽ‹Š‹Šˆ‡‡‰D
+ + + + + + + + + + + +
^Љ„|b`cfklllgo“§®±´¶¸¹»¾¿¾¼½¼¼»¹¸·µµ¶µ¶´´µ´´µ³´µ³´´´µµ´µµµµ¶´±°¯¯¬ª¤ œ˜—–‘ŽŽ”šŸ¯e ,,žÅ¾ÂÄÄÄÄ~hl˜ÑÊÉÇɽr’ÊÉjhfdbgrŒ«ÇÉÂÁÁÀ¿¿½»¼»º¹¹¹¸·¶µ´´¶·¸¹¼¿ÁÂÄÅÇÉÊÌÍÍÌËËÊÊÊËÊÉÉÈÇÅ¿½¹·´¯ª§¦§§¥Ÿ–‚qiikjkqtlh+ + + + + + + + + "#$%%$#'&&‹Ž‹ŒŠ‡†ˆ†…ˆE
+ + + + + + +
d•Ž‹‡jbeghllmjjx‹™¢©°µ¶·º»½¾¾»»½¾¼¹¹¸·¸¶µ¶·¸¸·µ···¹¹¹¹¹¸¹¸·¸¸¸···¶µ³³±¯ª§¢žš˜•‘‘‘’”œ–*%,)\Ä¿ÀÄÃÃÇ\)//}ÐÈÅɼE.MÃÌ«312:>;5.6^¡ÍÇÄÅÄÄÄÃÃÁÀ¾¼¼»ºº¹»»¼¾½ÀÄÅÇËÌËËÍÍÌÍËÊÉÉÈÈÉÉÉÊÈÆÂ¿»¸µ³±®«ª©¨¨§¥ ›xmjkkknuvml/ + + + + + + + + + + + + + + + !"#$$$%&&&(ˆ‰ŠŠ‰‡…‡‡„ƒC
+ + + + + + + + + + + +k•Žˆodfgikklkjp} §¯´¶¸º¼½¾½»¼½½»»º¹¸¸·µµ¸¹¹¹¸¹º»¼½½¼½¼½¼»»»»º»¼»¹·¶µ´°®«¦¡ž™••–•‘œa%$**•Áº¾¾¿Çy076,nÉÈÃÇ]6?±È»H6G«º·³–b-3vÊÉÆÇÈÇÇÇÆÆÆÅÃÁÂÂÁÁÂÄÅÅÈÊÊËÍÍËÊÌËÊËÊÉÈÈÇÇÆÆÇÆÄÀ»·³°ª©©§¨§¨¨¤¡œ“‚qlilklqwxnk1 + + + + + + + + + + + + + +"#$#$%&&((‰‰‰Šˆ†„…„ƒ„? +
+ + + + + + +o–‘މ‚qcceggijjiiuˆ˜£²µ¸¼¾À¿À¿¿¿¾¼»»º¸¶´³µ¸ºº¹»½¿¿¾¿ÁÁÁÀ¿¾¿¿¿ÀÀ¿¿½»º¸¸¸¶³²¯ª¦£ž›š˜–‘/!&!L´²¶¸»Ä2622,kÌÄÉn36›ÆÅ[1A¹ËÈÉÍÎ’8+eÉÉÇÉÈÉÉÈÉÉÉÈÇÈÉÈÇÈÉÊÊÊÊÌÌÌÌÌÊÊÊÊÊÉÈÉÇÆÅÃÂÁ¿»·±®ª¥¥¥¦¢¢¤¥¨¨¥Ÿš‘{mllnlnt{|pp5 + + + + + + + + + + + + + + + #$##$%$'('ˆˆ‡‡‡„ƒ„ƒ‚B
+ + + + +
u—“‘Š„vbccddfggigk~’Ÿ¨¯´º½¾¿¿ÀÁÂÁÀ¾¿½½º·´´¶¹»¼ºº¼¾¿¿ÀÁÁÀÀ¿¿¿¿ÁÂÁÀ¿¿¼½¼»¼¼º¹¶²¬¨£ ž˜œj.r!†¯¬²³¾-1RR10lÈˇ23ˆÇÈo25¢ËÅÅÄÅÐ6.ƒÏÆÇÉÉÉÉÉÊËÊÉÉÊÈÇÉÊËÊÉÊËËËÊËÉÉÊÊÉÉÇÆÂÀ½»»ºµ±©¦¥¢Ÿ ¢¤¦¨§¦Ÿ•ˆsonnnlou}|ot= + + + + + + + + + + + + + + !!"#&%%$&‡††…„‚ƒ€}}:
+ + + + + + +
!™”‘‹†{ecc`adffeeiu‰™£«±¸»¼¿ÀÀÂÄÄÃÁ¿¾¼¸··¹¼¼¼½¼ºº½½¾¿¿ÀÁÁÁÁÁÀÁÂÁÀÀÁ¿¿¾½½½½»¸µ³®¨¤ ™—7XžEA§£§¬´)+^¬<4-xÏ–32qÉɆ03‡ÎÅÇÆÄÃÅY1E¼ÉÇÈÉÈÈÈÉÈÈÇÈÈÈÇÇÈÈÉÈÈÊÊÊÉÉÉÇÇÆÆÅ¿»¸·´²³¦£ ŸŸŸœ››ž¡¤§§¦¥›“}npoonmpy€{qx? + + + + + + + + + + + + + + + +
"#$%%%&&……†ƒ~~|}4
+ + + + + + + +
%…™–‹…{hcc_`ccdeegk|ž¦¬²¶º¾¿ÁÃÃÃÃÁÀ½¹¶¶¸»¼½¿¾»»¼¼»¾¿ÀÂÂÂÂÁ¿ÀÁÂÂÂÁÁÂÁÀ¾½½¼»¶´³¯ª¥št!‚w}¢¤¬Š((RÑ/4,ª72_ÇÆœ56iÍÄÅÃÂÀÈs44™ÌÆÇÈÈÇÇÇÇÇÆÆÇÇÆÆÆÇÇÇÇÆÈÈÆÇÇÄÂÁÀ½¸µµ²²°«¥Ÿœœ™š™™šœŸ£¦¦¥ ˜soqrrpnr…}szE + + + + + + + + + + "$%$$$$$…„„‚ƒ€~}z|8
+ + +
&†–”†|hcba`abcdfgem|–¢§¯³·¹¼¾Á¿¾¾¾½»¹¶¶¸¹·ºº¹¹ºººº»½¾¾¾ÀÁÁÁÀÀÂÁÀ¿¿¿¿¿½»½»º¶³±¬¥EO”ŠŽE>˜•š ƒ%#I±¶r'-1|;2N¼À¨?7PÀÁÀ¿ÁÃÇb21”ÉÄÅÆÆÅÅÅÆÆÆÅÆÆÅÄÅÅÅÄÄÄÃÃþ¾º·´²®®«©¦§¤œ™–•”’’‘”—šŸ£¦¥¢š“€lpssrppv‚ˆ|v|P + + + + + + + + + + + + + + + + +!#$$$%&%‚„ƒ€~~|}|{yz=
+ + + + + + +
'ŠŸ›–މ‚kcbb`_``cefedn†›¥¬²µ¹»½¾¼º¸¸¶¶··¶´³³´µ²´µ´µ¶·ºº¹ºº»¾¾½½¼½»¹¸·µµ²°®¬«©§¦¤žž‡azvtYlЇv; Ÿ¦B %%*,<¯»±A2=´¾½½ÀÊŒ67>¬Á¾¾¾¾À¾¾ÀÀÀÀÁÀ¿¿¿À¿¾¾¾½½¼½¼¸µ²¯¬¨¥¡ Ÿœš•‹Šˆˆ‰Š‘•›Ÿ¢¤ œ“…roqsrqpr|†Š}z€Z + + + + + + + + + + + + + + + + + !!"%%&'%‚„€~|{zz|{xz?
+ + + + + + +
+ŽŸš–’‹ofdb``abbdefghwŒŸ§±¶»»»¼¹¶µ³³µ·¶³±®¯±²³³¶¶¶·¸¸·³°°°¯«©¨¤¢¡žœ››˜–Žˆ‘D*|y~m-“’Ÿˆ# ##%1¥´±E-.¢¾¸»²0-&c³¯³±±³³³´³²±²³³²²²³³³²²°°¯©¤¡Ÿžœœ˜—•’Š…ƒ~€ƒ†‰”™Ÿ¤¦¦£™Švmortrqqu€‹‹}~…d + + + + +
+ + + + + "%%%&'€}}|z{{zwv<
+ + + + + + + +
2‘𗓆thfdba`abdefhej“¢¨¯³¸¹¹¹¸¸¸¶¶¸¸¸´±®¨§¥¤£¥§©§©««««©«««§¢¢¡Ÿžœš™•’Š‹Šˆ……~pKyte*„ƒ„’c#Š¡ =^sfT80„–‘“—™››œœž £¢¡£¢£¥¤¤£¡ Ÿžš——”‹ˆ…„{{}€‚„‹•šŸ§««¨¢”‚qnprsrqsx„ŽŠ{€†g + + + + + + + #$$&&'}}~€|{z{xywuC
+ + + + +
+
2’ž›˜’ˆ{jjhda__abfehhfr‰¦°´·¸¹¹º»½¼¼½½¾»¶²ª¤¡žœžœ›ž ¢¡ Ÿ ž—ŒŠ‹†‡„ƒ{|{vxzussrxB$T[]\]^^L!Zja nsrsx?]rx1">gonsz{ƒ††‹‹Ž‘‘“”—™›žœœœš—”‘Ž‹ˆ…~}{|z{€ƒ‡”—ž¢¦®¬¦šŠtmorssrru|‡”‹}ƒ…d
+ + + + + + + !###'('}}~}|zxwuusuE
+ + + +
2”Ÿš“‰{mllgcb`abachiho|‘¡¬²¶¸»¼½¿¿¾ÀÁÀÀ¿º´°©¢›š˜™š››››˜–“‹{yyywvtttrrqoolkje<59[kiiffffj]EC@\e@9:_iihhkI+*)Vfg?,1449AIS_hegjmqru{|y{z|€€„‰“—šœš˜–‘ŽŠ†„~{yz|}~„Š˜›Ÿ¤©¬¯®©¡“|mnpttssrv€Ž•‡€‡Œh + + + + + + + + + +!##$')){|}|{yxturquP +
+ + + + + +
7— ž›–Šqqnleb_abcceikio‡œ§®´¶º½¿ÀÀÁÃľºµ±«¦¥¡ž›˜˜™š˜™˜”‘Œ„zvtutsrqrqnmmkhgea^]``^]^[^]^``aaa`^^_`cadeddcfcbb```_^`baaaababdfiknoqtutuuwzzz„Š‘•šœœœš—”‘ŽŠ‡„€~|zz|~‡Ž’™ž¡¦ª«°°¦›smpqtuussy‡””„‚Šo
+ + + + + + + + + #$$&(+|zy{ywttssorR +
+ + + + + + + + + +
9›¡ ˜’‹ƒvtumhea`bdcehjhiyœ§¯³·½¿ÀÀÃÄÃÂÿ½¹µ¯¬¨¡ž›š›œ›™—“Œ„}wrrsqoponjijffggecdddefecddefgeddcbdcddcdedccabb````^^]^`^^_aaddeilmoopqqruwy{}€ƒ…ˆ–ššœš—–‘ŽŠ‰ˆ†‚~{|}}„ˆ‘”™ž£¦«¯²¯£”~lnpsstuuv–”„…Ž’u + + +!#%%'+{{z}vvtssrpmI
+ + + + +
+
>¢¡˜“Œ†zuwrnhdabdcehiihmƒ”œ¦®µ»¾ÁÂÄÅÄÂÂÀÀ¿¾¹³¯¬§£¡Ÿž›™˜–’І€{wvrppommlnmmmoononnmmopmlmnmjjjhfghhhhghifefeddedbab```a`bccdfdejnpomoprsw{}~€ƒ‡Š”›žœ˜•“‹ˆ†…ƒ~}|~‚‰‘—œ ¥ª«®¯³²°¬žŒomprtutuvyƒ’™•‡’–x +
+ + + + + + + + + +0!$%&&+yyzyuvssqpnnP
+ + + + +
BŸ¡¢ž™”‰}u{yrmgcbcdegjjlku…–¢©±·»ÀÂÄÂÂÁÀÀÁÂÁ»·´°¬¨¥££¡šš–’Œˆƒ|zwttrtvwxwwvvwvvuvtrqsrqpqqpponmmmnlmkjjikihghhgfddcccdcdeefghjmoponqsw{~‚ƒ†‹ŽŽ•š¡¡žœ˜”‰‡…‚€|}}„„Š”™ž£§ª¯±°±´²®¥–zkoqstuuuv~Š—œ”‰•˜y + + + + + + + + * "#&&)+wxwvttrrollkW
+ + + +
J¡¢£ž›•މ€uz{uqleabcdfijkkm{™£¬³¸¼¿¿ÀÀÁÀÀÃý¹·³°«©¥¢ Ÿž›—”“ŒˆŠ‡‡„€€€„‡ˆ……‚‚‚€~|{yzxwxwuuttttrrrssrqnmmonmlllkihhhhiiikikmnpqsusrvz‚ˆ‰‘““•˜š›Ÿ¢¢ ™–‘ŽŠˆ†ƒ~|€‚†‰Ž–œŸ£¨«®±´´´´²«‰qmosutuvvz‚‘›Š“—šz
+ + +
+ + & "#&()+-ywttrrronlkhb + +
+
N¢¢£Ÿ›•‹„vw|yuqkcbccegikkkr‘§®´¹»¾ÀÀÁÂÂÃÄÄ¿½º¶²®¬§¤¡ œ˜–”“‘“Ž“šŸœœ™˜™—•‘‹‹Š…„‚~}}|zzz{|zwvuvuvvuqsqqppnpqosttwy{ƒƒ„†ˆ–———˜›œžžŸ¢£¢Ÿœ˜“Žˆ…„ƒ€}{~‚†Š•›¡¦©«¯²µ¶··³£‘tnoptvvwxw|ˆ˜œ‹Œ–˜{
+ + + +
+
+ + + + + !' #&*)*+/ttrsrpmllkjfd
+ +
+
P¦£¥¡›•ˆxx~|ysngabbdghjlmox‹— ª¯µ¹»½ÀÂÃÃÄÅÅÁÀ¼¹¶±®ª¦ œ™–””“‘”••˜™£ª±´´²±®ªª§¥¥¦¤¦¥¤¡ž›œ™˜•””’‘“‘Žˆ†…ƒ‚€€€~~€‚ƒ‚‚„††‡ŠŽ‘™Ÿ¡¡ ¢£¤¤¥¦£¢£¥¤ œ—“Žˆ„~~|}ƒ‡Œ‘—œ ¦©¬¯³¶¹¸¹¸±§œ„lnqswvwyzzƒ›Ÿš‡™››
+
+ + + + + + #'#&*,-.13trsqonljjihec
+ +
+ +
N§¦¨¥ž˜“‰}v|~zwqkdaccdgjkkmr}™ ª²·º½ÁÄÃÄÆÇÅÃÃÀ¾»·²®¨¢™–“’“‘’“’—ž¤©±·»»»»¸³±±´µµ¶··¸·´²±°°®¬¬ª§¥¦§¦¥¢ž›š™—•”””•–“’’–—™šš™™š›œ›› ¢¢¥©««ªª«¬«©¨¦¦¡™‘‰„||}~ƒ†‹’˜£§ª±µ¸¹¹··³«ŸŠtmoquwuvyz‰•ž¡–„‘™›
+
+ + +$&!'*-.035sqrpmlkhhgddd + + + + + +
M¨¦¨¥ œ–‘‹€x|~~{umhbcbefijjlpvƒ˜¢¬´º½ÀÃÄÅÇÈÅÄÄÄ¿º¸µ¯¨¢˜”“‘“•žª±·¼¿À¼»¸¶º¼¾ÀÁ¿ÁÃÄÃÃÃÃÀ¾¾¼½¼º¶¶¸·¶¶²±®«ª©§¦§©¨¦¦§¨©¯±³¯°°¯¬¬¯±²³³²±²²´´³²¯¬¨¨¥ –Š„€~~|}~ƒ†‹“™¤©¬®²¶¹ººº¶²£–ynopsvwwwy|‚œ¢¢‘†•œœž
+ + + + + + + + + &&"$+/0246pnqmmkffffcac(
+ + + +
+ + + +
M§¦§¤ ž™•…y|~€{tneabdfgjjkou{‡”ž¦®¶»½ÂÆÆÇÇÄÄÇÆÅÃÀ½»µ°¬¦¢›™•’“™¡¨±º¿À½»»¾±±ª§§¥¥ž—˜—–”—¥·ÂÄÀ½¾¿¿¿¿¼»¼¼»º¸··¸º¸·¶¹¹½¸ª˜‡„Œ•£´¿½»»»»½½ºº»»»¹¸·³°®¬¥œ‘Š…€~}„…‰”˜Ÿ¤¨´¶·»¼¼»¹´°§œ†nmpsvywvy|‰– §£‹†–Ÿ ¡~ + + + + + + + + + +)(#!#(/4567mmlhkjgeddcb`3 + + + + + +
S¤¨©¦¡™–’Šzy€ƒ€}xslb`bdfgjlnqv~Š“¥®¶»¿ÅÆÇÆÅÆÈÈÈÇÅÁÂÀ¼¸³¯«¨¦£¥ª²¹¼ÁÀ¿½»»¾¼RA=;;<;98<7476=K\¯ÆÄÀÀÀÀ¾¼½¾¾¾¾¾½½½¼»ÀÁ¨~X?320,.39KpžÂÇÀÀÂÃÄÃÂÁ¿½¼º¸¶´¯¥™„~}€†‹”™ ¦ª®³¸º¼¾¾½»¹²¬ tmmpuxywv{„›¤§¡ˆ‹™¡¡£z
+
+ + + + + +((&!#*46889jliihhfccc`\[9 + + +
W¨ªªª¥Ÿ›™•‹~w€ƒ€{tpgba`cgijnoru}ˆ“𤝵¼ÁÃÆÆÅÆÈÉÉÈÇÇÈÇÄ¿»¹¶µ´µ¶ºÀ¿¾¼º¹¹¹»±?9:99::8;:6779;;5.@z»Æ¿½¾½¼½¾½¾¿ÀÀ¾½¿Á¢f:+01/430111,*7VŒ½ÆÀÁÁÀÀ¾½¼»¸·µ²ªœˆ€ƒŠ‘—›£§±¶º½¿ÀÀÀ¾º¶¡–}lnptwzxxyƒ‰–¡©©›ƒœ¡¡¢w
+ + + + + + + +%)'"!!%17::<jhffgdd`a_[WU=
+ + + + +
[§©¬«§¡š–Žƒw~ƒ‚}wtjdb_`eginnprwŽ– ª³»¾ÂÄÄÃÄÅÈÉÈÇÈÉÉÈÆÅ¾¿¼¼½¾¿½»ºº¹¸¸¸»§63446776546::978874/JœÇ¼¼¼¼¼¼½½¾¿½ºÃµo6)021../210013793/M›Ãº¸¸¶´²°¯¬ª¤›ˆ„„ƒ‚…’•𠦬³µ¸»¾ÀÁÀÀ¾º´®¥•„topsvzxwz}€ˆœ¦«©•’Ÿ£££x
+ + + + + + + + + + + + + ((% "%.6;=@gdcda``\XXVRQD
+ + + + +
]¨©¬«§¤¡›•’†y~ƒ„ƒ€|wrhb_^`eilnnqtz„™£ª´¸¾ÂÂÂÄÆÉÊÉÊÊËËÊÉÇÅÄÅÄÂÁÀ½¼»º»º··¸º0...0.0012/41,/3345731‰Æº»¼ºº¼½»»¸Á¥L///00010//./0/04421+1y»·²±«¨¤¢žœ–†ƒƒƒ‚‚…‹‘”™Ÿ¥¬¯´¹½¾¿Á¿½¼¹µ¯©›‰wonrsy|yxz…‹–¡ª¬©‘ƒ–¤¦¤§p
+ + %R6%' !$%*17<>aa`_]^]WVVSRPJ + + + +
cª©¬¬ª§¢Ÿš“ˆ}}„………zunfa^^aejlloru{„–Ÿª·¼ÀÁÂÆÉÊËËÌÎÍÌËÊÊÊÉÉÇÅÃÀ¿¾½½¼¹¶¶µµ•+)))$PŒŒ‘‘„pJ.(10/311’ø¹º»»¼»¹ÂŸ?0111/00),044.++2530...(k¸±®ª§£ š•‹ˆƒ€€‚…‰”šž£©¯´¹½¾¿¿¿¾½º¶±ª‹{opqtw{{{z}„‰œ¦®¨‰‡š¤¦¥¥j
+ + + 3M3$$"%%%,38:___\[XXSSUQPLE
+ + +
c¬©®¬¨£Ÿ›–Œ~ƒ‡‡†‚}xslea_``ehjloqv{…‹•Ÿ®·½ÀÄÈÊÊËËÍÎÍÌÌÌËÌËÈÇÈÆÃÀ¿¾¾¹·µ±¯®'&$%q³®±²µ²²´²“K&-,..1;§»µ¹¹¹»ºÂ¥=23/11,);]™ ¡šŒmG--0,,.,"q¯£¢ œ˜”ˆ„‚€‚…ˆŽ”›Ÿ¤ª°µº¿¿ÀÁÁÀ¾¼¹³«¢‘soqswz|{{|€‡Ž• ª¯®¤‰ž¦¨¦¨f + + + + + + + + + + + + +#(1G2$%!$%'-138]\[XWVTRQPMLHB + + + + +
e«®°®ª¥ ˜‚~ƒŠŒ‡†‚|uphebb`bfhjlnsu{„Š‘ž«¹½ÃÈÊÊËËËÊÌÌÌÌËÊÊÈÈÊÈÆÃ¿¼¹µ³²°®¯Ž#$#$ p²¯°±°®²ºµc'-,--'Y¸´µ¶¶¸º¹L010.0++k§½¿ºº½¿¿Ã´:&,)&##$…¢™™•‘Їƒ€‚„†‰Ž•›¡¥©®´¸¾¿ÀÂÃÃÁ¾»·¦›‡vopquy||||„Š‘™¥±®˜Ž¢©¨¥¨^ +
+ + + + + + + + + + +&)*-@/!#"$%+115ZXVRRSROMLHDEB! + +
+
j««°°¯¬§¢žœ“‡€‰Œ‹ˆ†€xsmhfdcbahikmqruy€†Ž™¬·¼ÁÄÆÇÉÊÊÊÊÉÊÊÉÊÊÈÊÉǾ»¹¶µ´±®²#$#%#t´°³µ³°°´´·»]'.+,+-“º³³²³¼q,/-,.(5‹»¶´´¶¸ºººº»Ä«P"&!"?˜““ŒŠˆ†…ƒ„…†‰Ž•¡¦«¯³¸¼ÀÁÁÃÄ¿»·²ª|ooruxz|{|~ƒ‡Œ•Ÿ©°±«“£©¨§§W
+
+ + + + + + + + +&())/A,"$!%%)034VSQONOLJIIFEC? + + + +
+
fª¬±²°®¨£ ž–ˆ€ˆŒŒ‰‚~wpmifcbaaeimoostzƒˆš©´¼ÀÃÅÇÈÊÊÉÊÉÈÈÉÉÈÈÈÅÂÀ½º·¶¶²±²Œ$&#$#p³±³´³´´µ¶´·>'++,&]º±²±´Ÿ4**)+(5𷬮°±²´´³´´´²¶±Lz•Œ‹‰†…„…‡†ˆŠ”œ¡¥«¯³¹½ÁÂÃÄÄľº±ªžrmoswy}}||€…‹’›¤®±±¨†–¤¨¨§¥S
+ + + + + + +((()(+7*$ $%'/43SSOMLKLIFEDC?=& +
k«¬²³±®«§£Ÿ™Š€†ŒŽŽˆ„|vrkeddcaafklnqru}ƒŠ¬¶º½ÀÃÆÈÉÉÊÊÉÈÈÈÈÈÇÆÆÃÀ½¸¶µ´±³Ž$%$%$q²®²³´³´´³µµ³¹„'-+,*7§´²±·j#)(')&†¶«®¯¯°±±°°°°±®©ª›1P‘‰‡‡…„ƒ†‰Š”𡦩²·»¿ÂÅÅÅÅÃÀ»µ«ž“ƒspoqvy{~~|~‚‰— ©¯±°¡}„›¥ªª©§N
+ + + + +
))()))+1*"!"'/35QOMJJFGECAAA><. + +
p«²²°¯ª¦ œ€€ˆ‘‘Ž‹ˆ‚|wqiggfbbbeimpqsuzˆŽ—£²¹¾ÂÄÆÈÈÉÉÈÇÇÈÈÇÈÉÉļ¹¶µ±³Œ$%$%#t³®°²³´´µ´³³²³®;++*+*‰¶®±@"&&&!X°««¬¬°±°¯¯®¬«§¤Ÿ£g4‡…„†…„…ˆŠ‹‘–š ¦ª¯¶¹¼¿ÂÅÇÅÆÂ¿»µ¬Ÿ•voprux{}~~|…Œ”œ¤±´¯—|Œž§ª«§£E
+
+ + + + +
*)(()((*3*#!#-44MIGGHED@?@?=>:/ + + + +
r¨¬²³±°«§£Ÿ˜‡‡Œ‘’Š…€{vojggeda`fhlnorsy~~‡‘¡³¸¼ÀÃÅÅÇÈÈÇÈÉÈÇÇÈÉÇÅ¿½ºµµŒ$&&(%w¶±²µ¶¶·¹¸¶µµ´»Z$))+&m¹¯´—+&$$$+”°ª¬®¯±²±°¯®¬ª¨¤£Ÿš‰''€………„†ˆŠŽ‘”£¦¬±µ»ÀÃÅÆÇÇÅÄ¿»¶¥•„uonqtwy}~~}~ƒŠ– ©¯³²«Ž‘¢«®¬¦=
+ + + + +
#)()(()))*/-" ".33GFFDBAA?>>=<;73 + + + + +
sª«²´²¯««©¥ šŒ‚‚‹’“Œˆ†ztnihhfcbbegjmqsuv|€…‘¢«³¸½ÂÄÆÆÆÈÈÈÉÉÈÈÈÆÈÇÆÄ¿¹ºŽ%()*'~¹²²¶··¸»»¶¶µ³¹n())+'^¹²·~$'#%$Gªª«®®¯±°®¬¬ª¨¥¢ ™‘’;'|‡ˆ‡ˆ‰‹Ž‘•œ¡§¬´¹½ÁÆÈÊÊÉÇÇÀ¼º²§™†wnmptx{}~}}‡Œ“›¤²µ²¦…–¨¯¯¬§›;
+ + + + + + +#')(((*((''2- '/2GCAA?;<<=;<;876 + + + +
s¬²´´±«ª¦£œ…‚Š“”’‘Œ‡„yrmjihgebcejmprruz|€„‘ «²·½ÀÂÅÆÅÅÆÆÇÇÈÉÉÉÇÅÄ¿¼¿(-+,+‚¸³µ···¸¸·¶´³²·z&)')'U±«±j $#%!_«¥©©©ªªªªª§¦¤¢¡™•‘“G%}Š‹‰ŒŽ”˜œ£§¬³º½ÂÇÈÉÉÈÆÃ¿»·°§›‹|tkortx{~~~~„‹— ©±¶¶±Ÿ~‡›©®®¬¨›4
+ + + + + +
$(('(')('''(0-$,/DB>=<988987875/ + + + + + + +
oª«²µ´³°©§¦ –‡„Š’••”’‰†‚~wrnkjihfdegimppruxz{Š›¨´¹½ÀÂÄÄÅÅÆÆÈÉÉÈÆÄÄÃÁ¾Å‰-/+-.Џµ¶··¸·µµ¶¶´²¹%)&(%M®¨°W!"% o©¤¨¨©©ª©ª©¦¤¢¡Ÿš–”Ž“I+…Ž‘•—› ¥«¯·½¿ÂÆÉÈÈÇÄÄÁ½¸±¨Œ}ummptw{~€~~‡Ž”›£®³·´¯˜|Œ©®©˜, +
+
+
+ + + + +&('''%&&&'''(/+(,A?<<;999844543- + + + + +
{©¬±´´³±®«¨©¦™‰„Š’˜˜˜—”‰†‚{upnllkjhgfgkmpqqtvxz„•¡«±·¼ÂÄÄÄÅÅÅÆÇÇÆÅÄÄÄÃÂÈ|-210/–¾¸¹¸¸¹¹¹¸¶¶µ²ºs%&$&#P¬¤ªP #"«§©«©««ª©¨¥£¢¡ž›–“’E3’“••˜œŸ£¦¬±¶¾ÁÃÅÇÈÈÇÅÂÁ¿»´«¢’ulmrtv{}~€€€„Š’™ §°¶¸³©‡{’ ª¯¯¯©'
+ + + + + + + + +(''&&'&&'&$%&%/0%*<>>=9866543420/ + + +
0ާ®²³´³°¯ª©§„ˆ•˜šš—’Œ‰„ztollllljgghknqqrtuwyzœ¦±¼¾ÁÁÂÃÃÃÄÄÅÆÄÆÆÆÄÃÉo59:7:¦Áººººº»º¹·¶´±·Z!$#% Z¬¤ªQ!xª¦§§¨©©©§¥¤¤¢Ÿ™–“‘“=?˜•—™ž¡¥¨³¹¿ÄÅÇÉÉÉÈÆÅÁ½º´¬¦—‡yrmpuwx|~€€‡Ž—ž¤«²¶·³¢‚‚–¥°²°©Ž"
+ + + + + (''''&&&&&%%%$&+-$'::::754420//.-) + + + + + + + +
T›¡³µµ´±°®«©¨¢‘……‹’•˜˜—•’Œ…‚~ytommmnmkhiilpqrsttvxy~‰—¢¯¶»¾¿ÁÁÃÃÃÃÃÅÇÆÆÃÁÅ_89>=H²¾»º¹¹¸¸·¸¹µ°¯®?$$"$i«£«Zd©¤¦¦¦¤¥¨§¦¥£Ÿœ›˜–”—Š(Wœ—œŸ£¦«¯²·¿ÆÈÉÊÊÊÈÈÅÃÀ¼³«£—†zrnpuxx|€€„‹•œ¢©¯¶¸¶²—|†š¨¯²²¯ª…
+ + + + + + + + !('&'''&%%%%%%$$$-0$776753210.-,++* +
+
{¡ ¬³µ´³²°¯®«©¢•Š…“•˜™š™•‘‹†ƒ}xsppnmnolkkknprsstuusuz‰™¦´º½¾¾ÀÁÂÀÀÅÅÄÃüO:9<?O´¼¼º¸¸··³±±¯ª«“($!!%"§ ©lF§§§§¦¥¦§¦¤£¡ž›™——•˜iu›™Ÿ£¦©´·¼ÆÉÊËËÊÉÈÇÅÀ»µ®¥˜…|tlosyzz~€€€€‚‡‘𠦳¹¹µŒ{Šž«°±²¯¦}
+ + + + + + + + + #'''''&&&%%&&%%##%+/65455300/-,+*)(! + + +
6› ¬³µµ´²±°¯¬ª¤™‹‡Œ’˜œœ˜•‘Œˆƒ~yutronopommlprrsstustvx{…—©³¹»¼¾¿¿¾½ÀÂÂÂÁ±?6324M¶»»¸¶µ´²¯¬ª¨¤®_ ! /˜£ ¥„ %ª§©¦¦§§¤£¢Ÿžš™˜–••@+’ž¢§ª¯³¹ÀÈÌÌÍÌËÊÊÉÆÃ½¶¯¤—‹~tjlstwz{‚ƒ‚ƒƒ†Ž–ž£ª²·º¹´¥„|¢±²²®¨€
+ + + + + + + + + +$('''(('&&&&%&%$%#%,353310///,*'')&$ + + + + + +
\žžª°µµ´´³°¯®«§†Š—žŸœš–‘ˆ…€|ywsqqsrpopqqrrrrstutux}ˆœ¬²µ¸»¼½¾¾ÀÁÂÂÀ¨5.+++O»½º·´²±®ª¨¤Ÿ¢ˆ$M£ž ¡™/ b°©©§§§¤¢¡Ÿžžœ›™•žjO¤ž ¦¬°µ»ÀÆÉÊËËËËËÉÆÃÀ·£—‰}ulkotwxz|„„„„†Š’š ¨°µ¹º¹±š|~”¤²³³®¬{
+
+ + + &)))'&%&&%&&$%$%&%$&+.211/---,*('&&%" + + + + +
#†´¢©¯³´´³³°°¯®«£“ˆˆ”›Ÿ Ÿœš˜”†~|xutuvvutsssttstvvuxxz€¥¬¯°³·¹º¼¾¾¿À¿ÂŸ53,,,Y½»¸´°¬ª¦£•›4z šž£U! !)а¥¥¥¤¡Ÿžžžžœ˜‰.€§¢¥²µ»ÀÅÈÊËÊËÊÊÉÇĽ¸¤˜‰umkortx{~€‚„…„†‰Ž–¤«±¶ºº·¬{†˜¨°³³²®¨
+ + + + + +&***'%&&%%%&%%%$&(&&',/!001--.,))'&&&" + + +
?¦´¤œ©¯´´µ³³²°°°¦–Š…‹”›¡¤£¢¡ž›™•“‰ƒ€|zyyz{zxvvtuuuuxz|~€‚‡¥©¬®±³µ¶·¸¹¼¼Á‘5:497j¾µ²°¬¦¢Ÿš””„9=š˜™¢ˆ ! "#"7˜¬¢¡ Ÿžžœ›˜˜”? A £¦«³µºÁÅÈËÌÌÊËÉÊÆÁ¾¶« œŽ~volosuvz}ƒ„……‡Œ•œ¢©¯²·º¹´§ˆ|‹ž¬²³³³¯¢„6
+ + + + + + +'++)(('&&&&%%%&%&&&&%&(1#/.,,++)%&&%$#
+ + + + + + +
b²´£©²µ´´³³±±±¯©œ…‡—ž¦¥¤¢ œ›™–“‘І…ƒ||~~}zyxxx{}€ƒ‰‰•Ÿ¤¦¨©«®°´´¶·À‚+4361w¿²¬¨¥œ››˜e)›•™ž¤J$""#7‡§Ÿšš™——˜˜›¡Œ@# ¥£©°·ºÁÇËÌÌËËÊËÉÆÂ»¶®¡šƒwninsvwyy|€ƒ„„…ˆ‹‘𠦮±´¸º¸±œ€ ²´³³®š€Y
+
!*-+)(((''&%%&&&'%$%%$$&+/$****&%&$$$"! + + + + + +
"‡·¸£ ¨®²´´²´³²²²°©Ÿ‘ˆ…Œ•›¡¤¤¤£ žœ›—•”‘‹‰„ƒ‚ƒ„„ƒ€}|‚†Œ˜£¥›“™šœžŸ ¡¥¨ª®±¯¹q'0/0,n±ª¥¡ž˜b9X˜“”—šžž¢(!#!! *f‘œœ™™˜šœ’h1 !Tª¤ª°·»¿ÆÌÎÍËÊËÊÉÅÀ¼µ®¥—Žƒxoimqtvxy{~„„…ˆŠ‹• ¦«°´¶¸¸¶«“{“£®²µµ²«™€y!
+ + + + + + +
%)++)(((''&&%%&&&&%%%$%%%(3$)(%$##$##" + + + + + + +
5¡¸¹¢ž§¯³´µ³´´²²²²¢”‰ƒŠ–šž¤¦¦¤£¢ Ÿœš˜–•–•“ŽŒ‹Š‹ˆ‡‡†ˆŠ‘™¥µ¶«˜’‘”——› ¢¥§§°]!&'&%,@DAC<90&G‘’•™›œ§t 2Sq}|{qV5 =ž¨ª²¸»ÀÄÉÍÍÌÊÊÊÇÄÁº´¯¤›‘†{qklqstvy{|~ƒ…ˆˆŠ’›¥ª®²´¶¸·´¦†zˆ—¦°µµ´±©–ƒˆI +
+ + + + + + + + +%*++)(((&&%%&%%&&'%$$$%$$%)/('#$&##$" + + + + + + + +
_¶»º¡©®²³´´´´´³±²¯¥˜‹„†˜ž¤§¨§¦¦££¡ž›ššš››˜–””“‘‘‘–¬¶µµ®©•ˆ„‰”’–™œžž§NNˆ‰ŒŽ•˜œ¦Z#%$! :—©¨®¶¼ÁÅÈËÍÌËÉÈÆÄÀ»µ«¥œ‘†~skkpsrtvy|~„‡‰‹‘™£©®±³µ¸º¸¯žœª°µ¶µ°§•‡o
+ + + + + + + )*,+)((('&'&&%%%%%%$$#%%%&'(('%%%##! + + + + +
!‡º¿º¡œ©°³³´´´´´´²¯§›‡„‰’›¡¥¨¨¦¦¦¤££¡Ÿ ¡¡£¢¢¡Ÿžœ›ššœ ®µ´¯©¢–…|{|~†‰Š’““–C'b‡†ŠŒŠ‹Œ–˜›¢WBœ®¨¯´º¿ÅÉËËËËÊÈÇÅ¿º´«¤’„}ukmprrsvyz|~ƒ‡‹Œ’˜ ¨±³´·¹»¸—}‚‘ ¬±µ·¶²§”†-
+ + + + + +")++**)))('&'&%%&&&%$%$%%%&&%&$$$""" + + + + +
+
9¡»Á¹¤œ§®°±³³³´´³³³±«Ÿ‘‰…†Œ”𠤦¦§¦£¤¤¤¤¥§¦©§©ªª¨§¦¥¤£¥¬²¯¦”Žƒzvrsuwy}ƒ……†…‰6'Rˆ„†‰‡‡‰Š’–˜˜™—h" W ¬ª°´ºÂÇËÍËÉÉÈÇÅÃÁº´© šŒztlmorrsuwz|…‰“•œ£ª°´µ¶¸»º¶¦‰{Š—¡«±´·´°¥’H
+ + + + + + + + !*+*+*))(((&&&$%''&%%%%$%%%&$%$#"! + + + + + + + +
X³½Ã·ªš©°±±²²³´³´´²£•އƒˆ—¡¥¦§¦¤¤¤¥¦¨©«®¯°°±±±±°«ª¬¥™†€{upnmnopquxz{zyzy=-Ce{‡†…‡‡‰‰‹‹Ž”™˜•”‘•C<®©ª³·¾ÃÈËÌÌÉÈÇÆÅÃÀº´®¥›Ž€zvllnqrssuxy~„‰Œ’–›¢©°´µ¶·¸¹·¯šŽ›¤«°³¶´¯¤’–•“n
+ + + + + + + + + +!$*+****))((&''%%%%%&&%%%%%%$%$$# + + + +
~À¿À¹•¦¬²²²²²³´´´µ²¯¦™‘‰‚ˆ“˜¢¦¦¦¥¦¦¦¦ª«¯±³´¶¶·¸·¶´¯¬¥’ƒ~zxqnnlkkkloprssssspmfca\[X[[[Y]bju|†Š‰†„‡‰‹Š‹’’’’“•’’“’”’zJ))As¢ª³¸¾ÄÈËÌÌÊÈÇÆÅÿ¼µ±¦œ‘‚ztnknprssuuxz~ƒˆŒ“—›¡©®³¶··¸¸º·«“~…‘ž¨²µ·µ°¤š™–Ž8
+ + + + + !"',+******)((('('&&&('&&&%%$%&#$# + + +
+
.ŸÅÿ¸¯”¤¬²´´³´³³³³µ´²«œ’Š€ƒ‰‹’™ž¤¦§¨§§¦§ª°±³µ·¹º»»¹·¯©›‹€zwuqomjjiikmooopqpppmmklnnmpsx„†‹‘‹ŠŠ‰‰‹’“‘“”“““’‘‘““‘’•‡saOINW`v ¨§«¯³¸¿ÄÈÊËËÉÈÇÅÃþ·³«£˜‹ysokmpqrrtwvx{€†‹“–𠦳¶·¸·¸¹º³¥Š{†“¢©¬³·¸µ¯¤ •˜Y
+
+ + + + + +!(,+***+,+*)()(')(&'&&&%&&$#&&"# + + + + +
QºÄƽ·²—¢¬±´¶µµµ³²³µ´³¡—‚‚…†Š•œ¡¥¦¨§§¦¦¨ª¯°±²·ºº¼½½·¬©Ÿ’‹wvsonmkijkmnnnopoooonllmllmotz}…‹ŒŽŒ“–•’““”•““•“‘‘‘Ž‘—˜—˜œ¡¢££¦«®´»ÁÆÉËÌÊÈÇÇÆÄ¿½·°¬ ”ˆ~yrjlmnoqprvwxz„‰Ž’–šŸ¦²¶¹¹¸¸¸¹¹°˜~~‹š¦ª®´··´¯¡’£¦—˜v
+ + + + + "(++++**+)))))('(('&%&%%%$&''& + + + + +
{ÂÃŽ»¶– «°´µ¶µ´´³³´¶´¯¤š‘†ƒ†…‡Ž•› £¥¦§¦¥¦§¬¯±µ´¾ÄÁ¿À·«±¬©¢—Šzwtrpmnmnoopqpppooppomnopptw{‚†‹‹‹ŽŽŽ‘”––•–––––˜™—“‘‘‘‘““”••˜šœ¡¦©²¶½ÂÇÉÊÊÈÇÆÆÅľ¸±¨¢—Š€zrkonqromoqsy{~‚‰Ž”–›ž¢©±¶¹¹¸¹¹ºº¶§}‚‘Ÿ©¬°µ·¶³®œ¦©”Œ4
+ + + + + + +!(*+,,+*+*+)('''')(&&'%%%%&'&% + + + +
- ÂÄÅ»½¸“ž©²µ¶´´µ´´µµ´°¨œ“ˆ‚„„‡‹‘—¢¢£¤¤¥£¥¨¬µšPmš»Å¶¶··³¯¥œ’Šƒzvvsrrssusstrrstqqrtuuvuw|„‡‰ŠŽ’•––—˜™˜™˜™™™™–’Ž‘’”••—™›Ÿ¡¥«°µ¸¿ÆÇÉÊÉÈÆÆÅÄľ¹³ªž•Œz{kOB9ATjrqru{~ƒ‰Ž“–™Ÿ¢§´·¹¹¸¹»»º² …}‡•£«®±µ·¶²¯™“¨¬ ––V
+ + + + + + + +")++,,+***+)((('()('(''%&&&&&% + + + + +
M·ÂÆÆ¹¿¹–¨®²³´µ¶¶µµµµ´²« —Š‚‚„…„‹“šŸ¢£¤¤¤¤¤¦©´Z")0P¬Æµ¸º½»¸·´¥š•‹†}||||zzxxxxvuuvzyyyvvy~ƒ…ˆ‹”—š››œ››››š››—–‘ŽŽ‘””•—šœŸ£¦¬°µº¿ÄÉÊÊÈÈÆÅÅÅÄÁ¼¸±« ˜Œ‚z{_*$Inwx~‚ˆ’–™ž¢¦«²¶¹º¹¹¹º»·š‰š¤ª±¶··³¬–•¨¬£˜˜t
+ + + + + + + + + +!)++*+,***)((())(((&&&''''%%'% + +
rÅÄÉĹ»˜š¦®²´µµµµ´´´µ´²¬£šƒƒ„ƒ„‡Ž—›Ÿ¢¤£¥¤¢«”*(+)8¨È¶·º»¼¾½¾½»¸¯ª£Ÿ˜“‘ŽŒŠŠ‡…„„„‚€|yxyy|}‚…‰Œ’—›ŸžŸ¡¢¡££žœ›™–•”“’’•–•˜›ž¡¦«®´¹¾ÂÇÉËËÉÇÆÅÄÃÿ»µ®¦›“Š~wzV,f€†’—™ž£¥ª°µ»¼º¹¹ºº»µ¦}†’ž¦«°´¶¸¹³¬——ª®©›˜:
+ +"(++++,*)***+*))())'&&'(''&''& + + + + + + +
#ÈÅ˺Á¹›š¦±µµ´´³³³³´·¶¯¦›‘…‚‚„ˆ‘—˜™›ž¡¢£¡ª](*(=µÆ³¬¶¹»½À¿»Â³Ÿ§±¶·´©¨¥¢Ÿœ™—–•’‘ŽŠ…|zx{„ˆ“˜¡¥¦¥¥£¤¤£ žœš˜–••“”–™ššœž¡§«±µ¹¾ÃÆÊËÊÊÈÆÆÄÅÄÁ½¶°§œ‘Œ†{vwY!%k„Š‘•˜œ¢¦¨®´¸¼½»ºº»»º±ž‡‡— ©°´¶··³ª“𫝫Ÿœ–W
+ $)*********+*))''))((''(&%&''(
+ + + + + + +
:ÇÈȽ¾Ãº˜¦±³´¶µ³³³³´··³©”ˆ‚}|Œ”––˜šœŸ ¢¡8"&'L»Á³¬´¸½ÀÁÀŸW?GRap†¯¹µ±®«©¥£¢Ÿœ™–“‡}|}~‚‰–›ž¢¥¦§§¥££££ žœ›š™™˜™šžžžž¡¥ª®³¸½ÂÇÊËËÊÈÇÆÅÄÅÄÁ¿½µ¦—‰ƒ{uwa'8ˆ•™œ ¤§¬±·½¿¾»º»¼»·«’€Š˜£«®²µ·¸¶±¦«±¢ž›s
+ + + + + + +$)+++++++))*)*,((((((('&''%'(' + + + + + + + + + +
S¿ÄÊɼÂú–£¬¯²´µ¶´²²´¶¸¸´¬¡–‹ƒ|zx{ƒ“”—šš›š ‰ %%S½º°ª°¶½ÁÁǽ]088485H²Â¼º¸·´¯ª©¤¡Ÿœ˜“Љˆƒ†Œ”›Ÿ£¥¥¦¦§¤¢¢¢¢ ŸŸžœœŸŸ¡ ¡¥¦§ª¯³¹¾ÀÅÈÉËÊÉÇÆÅÅÄÃÅ¿¸whed_x}ttm,E’“–›Ÿ¡¦«¯µº¿À½º¹»¼º´¢‡„œ¥¬±³¶¸·µ°£Ÿ±¯£ œˆ3
+ + + + + + +%+*)**)*)'(())*'''('&&&&''&''% + + + + + + + +
sÆÇÌ˽Âļž’¢¬¯²´µ¶¶´²³µ·¸·°¤™…€~zxx}ˆ“•——– m Z¼²©¦«±¹¼ÃÂ`0234367\ÃÄÂÂÁ¿¾»¸´°ª¥£¡ ›’•œš˜Ÿ¤¦¨«ª©¨©©§¨¦¥£¤¦§¥§¥§©¨ª®±·»¿ÄÉÌÐÓÒÐÐÍÈÅÄÃÃþ¼«:!kxrr8+Pae\?}—•™¡¥ª¯µ¹½¿¾½ºº¼¼¹®—…‡•Ÿ§®±µ···µ¯Ÿ¢®²°¦ŸŸO
+ + + + +'+)))***('()))((())'&&%'''(''& + + +
+ + +
(–ÊÊÌȾľ¥“¢¯²´µ¶¶µµ´¶¶¸·²§š†€€{vuu{†‹‘’”œVR³¨¡ ¤«²·¿i35465572zÉÄÆÅÅÄÄÿº¸·³®ª±{9BGQs¬©¯¯°±°°¯®®¯¬®¯±²±³´´¶¸»ÁÆÇÁº´©œˆ…†™·ÊÇÂÄÂÁ·´Ÿ/2tpyS?mtuv|‚f(Vš–›Ÿ¡¥¨³·»À¿¼¹»¼»³¦ƒ‚Œ—¡ª¯²µ·¹·´«š’¤³±§Ÿ –l
+ + + + + + + + +
'+**)+*))***)))**)('(('&'&)('% +
+ + +
<²ÈËÌÅÂĽ¤“ ¬±²´¶µ¶µ´´µ¶¸·´«“„€|xttu}ƒ†ŽŒŽ‘A6žŸ™šž¦¬¸|,311566:;–ÅÂÅÆÅÅÅÅÅÅÄÃÀ¾¹¸ªE-,-+Bµµ··¸¸¸¸¸¸··¶¶¶µµ¶º»¼½¼¿Çȼ fYNG@:62118M¹Å¿½º¯ª#Nqqc"Grosw|…j<œœ £¥¨¬¯³·¼ÁÁ¾¼ºº»¹¬ŸŒ‚„›¥«±³¶·¸·²©˜“¥®²±©¡¡“y.
+ + + + +!(*++*))+)*+))))))(()*)('(''&%'
+ + + + +
+
\ÁÈÌËÃÂÅÁ»§“ª±´µµ¶¶µµ¶¶·¸¸´¬¡˜‹‚€~zvrruz‚‹Œ‹6{š’”™ž©Œ.)*,./37:F¯ÁÁÃÃÃÄÄÅÆÇÈÈÆÄ¿Ãk43.,,2šÁ¼½¾¿ÀÀÀÀÀÀÁ¿¾¿¿¿ÀÁÂÂÅËÄŸsU<6533332//.0/25R¤À¶²¨§t!gln1?uoty|ƒ‰Š’‘Ÿ ¤§¨©°±¶º½ÀÀ½»º»º´¦“‡„ˆ’¡ª¯²´µ¶¸¶±¦”—§¯²²¬¡¤˜„Q
+ + +
!*+***+**)**))*)))'(())('''&'&( + + +
+ + + +
ƒÅÇÌÊÃÄÇý¬”™¦¯³¶···¶¶···¸º¸°¤™‡ƒ€{xuqpsy}‚‡‰‰/F‰”™Ÿ9'&(,,/56WÀÀÁÃÃÂÂÃÆÇÇÇÆÆÅÏŠ8630163xÇÃÅÆÄÄÆÆÆÆÆÅÇÈÇÇÇÉÈÉÏÉd=54642-,'')),020243A•µ§ œXBpmL.tuv|†‹Ž’˜¡¦¦ª«««¬¯³¸½ÀÀ¿½ºº»¸¯œŠ„†Žš¥¬°²´µ¶·µ°£’œ§®´´°¢¥ Žj
+ + + +!$*.+,+,,,*)****)))**)**(()('('%' + + + +
+
, ÅÈÍËÃÇÈľ¬”˜£±µ·¸·······º¹´¨œ“‹ˆ…|ytpnqvy~€‚+!!r‡†Ž˜e !$%).45nÁ½¾ÁÃÃÂÃÇÇÆÆÅÃ΢?5210148S¼ÈÈÉÈÉÉÈÇÈÉÇÈÉÈÊÊÊÌÑf=6651++>TgtthR4-42.00.‡®‘‡:"fmi'axw~…‹•–œ ¤§©ª«ª««¬°µ¼ÀÂÁ¿¼»»¼¶©”ˆ†‰’Ÿ©®°²´´µ¸¶°£“¨±´´¯¥¦¥“y-
+ + + + + + + + +
33-,,,++*+*+*)*)))++**)(()&((&' + + +
+ + +
=³ÈÊÍÉÅÉÈĽ®—™¡©°´·¸¸¸·¶··¸¹¹¶ž”Œ‰‡†‚|wsonpruvx+,F=†ƒ‚) %*/3À»¼ÀÁÃÂÃÆÆÆÆĄ̈F4/..-.129ŸÌÈÊÊÊËÊÉÊÊÊÉÉÉÊÉÎÇ‚F523/*<d•°½ÃÁÁÁ½¥d,*,+&q¯¢—Šn"AnoM8~z‰”—œ ¤¦§©ª¨§¨ª«®´¹½ÁÂÁ½»»¼¹°¡††‹—¤¬¯¯±³³¸¹¶°Ÿ’ž©²´´±§¥¤Ÿ‰G
+ + + ++81,,++**+))*++)()***)((('&(('(
!% + + + + + +
RÀÉÉÍÇÃÉÈľ¯›–Ÿ§®³µ·¹¸··¶·¸¸¸¶¯Ÿ”Ž‹‰ˆ†€{uponmoor;%jlƒ]!%+3”¾»¼½¾ÀÂÃÅÅÄÃ˳K50/-++++,+rÍÉËËËËÊÊËÌÌÌËÉÈи_5824,/i¯ÇÈÄÁÀ¿½»»¿¿ƒ-(&k±¤™‰S]jn0]„„‹‘—š £¥¦§¨©©§¦§¨¬±µ¼¿ÁÁ¿¼»½¼¸«˜‹†ˆœ¨¯°°±´¶¸¸´®›’¡«²³µ²ª¤§¢”g
+!"+38--,++++**+-+()()*++)((''((((
!$'),/ + + +
rÇÉÌËÄÆÈǽ°œ–ž¦²µ·¹¸··¶···¸µ¯¤™“ŠŒŠ„~zsqommmqU oB?ˆ01%1¡¾¼½¿¿ÀÂÄÆÄÁĸS/110*))))+*H¼ÍËËËËÊËËÌÌËÊÈѱM4231+K¡ÉÆÄÃÄÃÂÁ¿¼¸¸¸¾…1d®¤œŽ‚}4.nmg x‹—› ¤¦¦¦¦§¦¦¤¥§©®±¸¼ÀÂÁ½»¼¾»´¤‘‰‡‹”¢°°±³¶¸¹·³«—”£¬±³µ´¬¥ª¦žƒ<
+ + + + + + + #*.69-,,,+*)*)*+**)))*,+())))()'"&,-/005&
+
*—ÈËÎÌÅÈÉÇÁº°œ”›¦±´·¹¹¸···¸¸¹¸±©œ“މ~zvsmnnmj%XpolD[3¢¸¶¾ÀÀÀÀÂÃÃÅÀ_3522-9I'*+./ŽÐÈÉÉËËÊÊÌËÊÇͳO3311)`¾ÌÄÄÅÄÄÂÀ¾¼¸µ´±¯¯”¨¥Ÿ–‹~m%Dwu[0‰Œ‘™ ¢¤¥¦¦¦¥£¤¤¦¨°³¸¾ÁÃÀ½»½¾¹¬™Š‰‰˜¥±°²´¶·¹·°§–—¦±´µ´¦©¨£•^
+ +
!! #%+,-:7,-,**)))*,*)*))*))***))((' $)+-0101* + + + + +
?²ÆÍÐÍÅÊÉÅ¿¹¯ž”—£«¯²¶¸¹¹·¸¸¹¹¸¸´« •‘“”‘Žˆ‚€|wuqomp::yO8=xc5¢¬®´¹»»»½¿ÀÊ|4:6479—σ(,+-.XÆÇÈÉÊÉÉÉËÊÈÈÆ]3400(eÄÆÁÂÂÂÁ¿¿¼¹·´²°¯®¬¬¥Ÿš’‡‚eS…ƒT;’˜Ÿ¡¢¤¤¤£¤¢¢¢£¥©¬±µ»ÁÃþ»¼½¼²¤‘‹‹“ž§°°±³µ¶¸¸µ¯¥•™§¯²µ¶´¯¨©¬¥•t, + +! ").-,181,+*)**+++)*+*(()(),)(*('( ""$'*+..1200. + +
^¿ÅÍÐËÆÌÉþ¸± •–¡ª°´µ·¹¹¸¸¸¹ºº¸µ¬£™‘’—˜”‰ƒ€}zwrnpO%kv,CŒi2𤧲´µ¸¸¶Àš866675|ÇÄ7../17¢ËÇÉÈÈÈÇÉÈÅË‹4402/I»ÂÀÁ¿»¹¸¶¶´³°¯®««©§¦¢›–Œ‰eX“Y:˜˜›Ÿ ¢£¤£¡¡ ŸŸ¡¤¥©®²·½ÁÃÁ¼»¾¾¹¬šŒ‹‹Œ˜£«°°²³¶¸ºº´¯¡’›¦®³µµ´±ª¨°ªš…P
+ + + + !!&..,+49/++**++*+)***)()((+*('(()!%()))+.///13331
ÆÈÎÐÊÈËÇþº´¤—•Ÿª°µ··¸¸¸¸¸¹º¼»¸¯§•”š™“ŽŠ…ƒ€}xstaWyalˆm.”ž¡¦¬®±²³¹´I*0-/.Wº½¼¿Y-2013pÍÇÇÇÇÆÅÆÆÈÂ[21041ü¾½»¶µ´²²°®¬«¨§§¤¡žš”“‹Œj R™“g0”Ÿ ¢¢¢£¤¢ ŸŸž ¢¥«°µº¾ÁÁ¾¼¼¾¼²£”‹‹Š›¦®²°²³¶¸º¸µœ‘›§¯´µµ·³©±ŸŠt& +
+ +##$#!#+,./-.65--+*+*)****)**)*)))))))* '''&(0./20021/445558
*žÈÊÎÎÉÉËÆÂ½»¶¥—“𥮴¸¸¸¸¸··¹¼¼»¸³ª ™‘—š˜”Œ†‚zvn$Auu=6z|p/“¡£¦ª¬ª·u%)*)+2™º¶·¾€*1//1I¹ÈÅÆÅÃÃÂÂÆ¬>3./3Aº¸··¶³³²°®¬«©§¦¦¥¢ž›—“‘‹Œl H˜”w!!ƒ¡ ¢£¤¢¢ Ÿ Ÿ¢¦®´¹½ÁÃÀ¼»½½¸¬šŒŠ•ž¨¯³²²µ·¹º·³ª–©°³µ¶·¶¯©²¯£F
+
##$##%,,,/0.164.-,+********++*)))**(''((+.0/4956:758;89=@9:;7
B±ÇÎÏÍÉÊÊÆÂ½»¹§—“˜£«±¶¸¸¹¸·¸¹»½¼ºµ®£›’‘˜š˜”‹†„‚€{x(/rrkW}{y%/ššœ ££¤¨œ3$&%'%c·±³µ¹¢1,.,14ƾÁÁÁÁÀ¿Å21,+0T·´µ´²±°°±¯®¬°®®®¬¨¤žš“‘‡†‹‘z$<”“%d¥¢¤¤¤£¡ ŸžŸ” ®°·»½Á¾¼¾½»³¤”ŽŒ‹Ž–£¬±±±²µ¹º»¹²¦‘’ ª²µ¶¶··µ«®¯¥‘…f
+ + + +$##$$&-/.11-/172-*-+*****,*)()*))***''(),1-.11011345578<<==<5
V½ÇÎÎËÈÊÊÆÁ¾¼¸©š“™¢©®²¶¸º»º¸¸¹¼½¼¶°©Ÿ•Ž”›š—•’‹†…ƒ~}/'suyM#r}|}**„”•“”˜™›¤b 1›¯¬®®³S&*(+*_¿¹ºº¼¾¾ºÁ}**)(*]µ¯°®®¬¬«ª¨©s`b_[USKC<81`‘’‚)0Œ•Œ,:¢§¥¤¢ œœœœ JV¸¶¼¿ÂÀ¼½¾½º¬™Œ‹Œ“ž¨°±±±³¶¹ºº¶¯ •£´µµ¶¸¹¸°©™…z,
+ +%$$%%)-0/00-,-26.+,,**+**)**(**)))))'(,+-../00//3355565799:95 +
vÃÇÎÎËÉÌÉÄÀ¾½º¬”–¡©®²³·º¼¼ººº»¼¼¸°§¢›“‘–››™–’†„„…3"mzvu)E€…‰0*…Ž‘’‹,X¢¢¦§¨§®s$#'&5¡¸±±³¶¸¸¼r%%%#$T®¨¨§¦¦¤¤¢¢¢¡C!Q’‹/(„““@ f«¡¡¡ž››››˜¤w'$R«¿ºÁÀ½»¾¾¼³¤“ŽŽ˜£«±²²±³¶¸»¹³œ‘—£´µ¶¸¹¹¸°«¯¡ˆ€Q + + +&$$%&*///0/-,,-45,++++++++**)))()*)*'&3/000.10038766777596987!
&˜ÃÉÏÏËËËÇÃÁ¾¼º¯ –•ž¦¬°´¶¸»¼º¹º¼¼¼º´«¢–’’˜š›™—•ކ‡‡2!o~{}\f‚ŠŽ3+‡ŽŒ‹ŒŒ‘\$|”—›žŸžž’* #j±§ªª«ª«±m""C¥ žžœšš˜™œ›> T‹Œ’4!}‘•_ '~ª Ÿ››š™— ”1"'$D¾¿¾º¼À¾¹¬˜ŽŒ“§¯±±±²´·¸¹·±¨—˜¥³·¶¸¸¸¸°¨¯±©’€r
+ +"&$$&(,//0/.--,-.72++++)*(*))))))))**((6324301128;667:88:=8:9:( +
=¯ÄËÐÐÌËÊÆÃÀ¾½»²£˜”œ£ª¯´¶¸¹º¹¸¸»½¼¼º°¤›—’“•˜šœš˜•‹‹8"sƒ€~32„‡Œ‘1*„‹ˆ†…ˆ…25‚”˜œŸ¡šžQ2™¢¢¢ ¡¡¤o/Ž“’‘Ž‘–˜“7_ŒŒ’: y’•„'#.~§žšš™– œ='&(&~û¸¹¿Á½¶¥“Ž˜¢«±²±±³µ·¹¹µ¯¥–‘𥮳¶·¶¹¹¸´«°³¬œ€„5
%''&&),...-----,,091,,.,*)**())))()++**<8798549<9:6::<<=AB?@A@7 +
f¹ÄÍÏÎÌÍËÄÂÀ¾¾¼´¥™•›¡§®²¶¸¹¹¹¹¸»¾¾¾»³©—“‘‘’•˜š›š—•’’:!x‹‡ƒŠa^‘“–‘0(€Ž‰‡ˆ‡Žd(6@JOT[`cIh¡—–—™˜›z]‡‚„…‡ˆ‘”–’1_ŽŒ7 v–•žK! )qœ¡›ž¡‹>&%&%9®¼µ¶»Á¾¸š”𦮲²²²´¶¹º¹¶£’‘œ§®²µ¶·¸ºº¶¯³°£‹ˆ[
((*))+,///.-,-.-,,27.,,-,++*)))))()**)*<<=><?=BD@@>AB@CEEFEHHGO +
4™ºÄÎÏÍÌÍÊÆÂÀ¾¿¾·¦›“𢍮²³¶¹¹¸¸¸º¼¾¾¼¸®£™–““–˜˜™—–’4&}Š‹‡>‡™——˜‰&#yŒ‡†‡Š‹6-‹‘І}.,{‚ƒ‰”••H.--/-$fŒ‰†3"{“—Ÿ+ FrxZ&#"$$²²¸ÀÁ½°ŽŽ‘— ª°³³²²´¸»½»·¬ Ž’žª°³´´·¸º¹·®®²±©•ˆy
()*+,......--,-..-.22,,,+-+****)*)()(();<<?BDABEGDEIJFFGIKIILMT4
e±ºÊÏÏÍÌÎÊÅÂÀÀÀ¿¹ªž‘—¢¨®°³¶·¸¸¸¹º»¼¿½¹²¨œ—“‘‘‘’’”•––˜’.)‘“‹–˜š›™!q‡„„„‰lU†„ƒ~}€FC}}‚„‹ŽŽŽŽ‘’“•sm‡„0$~–™œ§t"$#!"$%_Àµ¯´¼Á¼µ§“ŒŒŽ“›¥²´³²³¶¹¼¾»µª—Œ” «°³´´·ºº¹¹±ª¯²¯†ŠG
')))(*.--.,-/,-013.063,+*,-*)*+*****)**==>DFEEFKLLJMNNOKNLMILR]C
.œ³½ÎÐÎÌÍÌÈÄÂÀÀÀÀ»®¡’“Ÿ§°µ¸ºº¹¸¹º»¼¾½ºµ¢™•“‘‘’“““”•••tH'-†“˜›šš™™~k‚‚ƒ‡?#xƒ€{{zkJ‚‚‡ŠŠ‰Š’•™˜”–tnЇ+&ƒ—› Ÿ¡b ""!!"#T¸¸®±»Á¿¸«›Œ—Ÿ¨±µ´³³µ¸»½¾¸°¤•Ž—¡«¯±´´¶¹¹ºº³ªª°±¤‹‹k +
*()((&).0/.-././12:82270,++++++++*))*+**BBFIJJKKNOQLNOOPNOMLOQPSD
\¹°ÁÍÏÌËÎÌÆÃÁÁÁÁÁ½±¤•’𥬲¶¹»»¹ºº»¼¼¼»ºµ°§ž˜”‘“––—•’“••œˆ}—“‘”™œœ›˜–‘{"`„€€„r!'M‚}zx|OG‡ˆŠ‹Œ“•–•”\!u‰†t#+…–œŸšŸV!""$#f½¹¯®µ¿Â¼²¤”ŽŽ”œ¥°³¶´³³µ¹¼¾¾¸¡”™£ª¯±±³µ¸º»¼·¬ª¯²¬”†ƒ.
#*((('(*.0//..////0<N6-47-*+**+**++)(*,*(ILJIKLJJLOSRQOOMMNOLONLIH
#–¿´ÅÌÍÊÌÏËÇÂÁÂÁÂÃÁ´¦˜“˜¡©°µ¹ºº»¼»»¼½¾½»·²«¢š••–˜———”‘’•–— ›˜–˜šœŸœ˜•y"W‡€~„D uƒuncYNJCA=81-*h{z|{xuy76rŽŽ’“–˜š˜š™s#$|‹‰r,ˆ—™œž Ÿ] !"$";‹Ã¾²¯³½ÁÁ¸ªœ“˜¡³´¶´³´·º¾À½µª‘š¤ª°°±´µ¸º¼»¹®ª®²®‡‹O
!'''((**-../.-//.,/1KK-.78**+)**)*+*))***LOPPQRQONOPSSRSPLNONJKNMM1 +
YÀ¸¶ÆËÉÊÎÏËÆÂÁÁÁÃÅÆº«š“•œ¦¬²¶¸º»¼¼»»»½¾½º´¬¦Ÿ˜•–—™š™—”‘’•—˜™œŸŸ™–—˜›™–’|%Jƒ~€v#D‡ƒ‚…ˆŒŠ‰ˆˆ‡†ƒ~|c7{ywxvtvv/ R„˜ž ¢£žŠW,ˆŒŒk*‡œœŸ œœo8!*I€²Ä¼·¯²»ÁÁ¼²¢’ŽŽ‘•›§¯´´µ´´³·¼À¿½²¥–’¤ª¯±´µ¶¸º»»¹±«±³©‡q
&''(''(+.../..-..-/15B7,/63,+*)()**,*(*+*PSSPSXTPSMPPRRSPNNLKMOQROA
šÆ´ºÊÍÉËÏÎËÆÂÀÂÃÄÆÇ½¬ž”•𢩝³¸º»»¼»»»»½¼»·°¨¢œ—––˜›››–“’“””’“˜žœ—–••”’'A}ƒWn‡ƒ…†„†‰ˆ‹‹‡…‚~{€AW}xxywy€x1%=[nxvfG*WŒ‰e)‡Ÿ £¢ œ™–š“v`Ycw—³¼¼»¹²²·À¿¶©—Ž‘”˜¡¬³³²´µ´µ¹¿¿¿»¯Ÿ’“ž¦«¯³´·¹º¼»»»µ©«¯²¯—‡‡='(''&((*.../00-,-./1034-,05/**+*,,++*)***WWWY]^[YXTXSZ]ZURRSQPRWTMK
VÅó½ÌÍÇËÑÍÇÂÁÁÃÄÅÇÉÀ° “”™ §¬±·º»»»»»¼¼¼¼»¸³¬£˜––™œžžœ—““’‘’’˜™™—”‘Žˆ†„?#7}|/?‡ƒ‡‰‰ˆ‡‰Ž‹Š‡ƒ}||t ,y}{}ƒƒ‰‚=E’‰ˆŒh&„žŸ Ÿœš˜——Ÿ¢¤¨ª±µ»»ºµ±·¾ÂÁ¹ž”‘’–œ¦¯´´´´´µ¶»ÀÀ¾¸©›•¡¦¬±³µ·º½½¼¼¼¹®®°±¯ŸŠˆd&))''()(,/.-010/.-/110/1/,-26,)(,..-,+***)UUUVXZ[ZXYYTY`^ZWY[YR\[YVZ$ +
—Ì¿³¾ÍÊÅÎÑÌÄÂÁÃÄÇÈÊËĵ¤•“–¥¬³¹º»¼¼»»¼½¾¿Àºµ¯¥ ›—–™œžŸ¡—“‘“•——•ƒ€~{vpmfj€‡B-$g‡‰Š‰ŠŠŠŒŒ‡„~{zULƒ€†‹ŠŒ““T:’ŽŠ‹i zŸš˜˜–—šœž¡¥¨¬¶º½¼¹µ´¼Â¼±¢–“‘”š¡«³µµµ´³´¹¾¿¿¼³¤•‹˜¢©®±³¶¹»»»¼¾½º±®°±°ª’‡|)*+*)**))....-//...020/-.//,,43-++,,,,+***)NOQQORRRSPQNMSPNQPQSSWVWVU5 +
N¿È¾²ÁËÇÆÐÐËÄÂÂÄÆÊÊÌÏÆ¹§—’”›¡¨°·¹¼½¼»»»¼¿ÁÀ¿¹²¨¢Ÿ›˜——›ž¡ œ—”“’’””’‘““Žˆƒ~~ƒ‹”™ž–‹€qjŠŒŽŽŽŽŠ‰†ƒ€~~4#r‡Š’“””—œy0?ŒŽ‰‹‰‹`({ š˜˜—–˜™œ¡¥¨®µ¼ÁÀ¼¸µ¹ÀÁ½³©›“’’•œ¦°¶¶´´´³µ»ÀÁ¿º¡’Œ›£¬±²µµ·»»»¼¾½º²¯°±±¯Ÿ‹†I ++***))*./--...//.2510.../-+-64.,(*,,++*)(PRWVPOQTXTTRQQLLQOOOSVSTTSK
’ÊÆ»´ÅÉÅÇÒÐÉÃÃÅÇËÌÌÎÐ̾«™“–™Ÿ¦¯³·º»»¼»º¼¾ÀÀÀ¼¸®¦¡š——˜œžŸœ™–”•”•”‘Љˆˆ‡††‰Ž”šœž£¥¥£¢ž—–•––”“”‘Œ‰‡„€‚x][\[Z]|‘“—˜šžž Ÿ¥š_&U’ˆ‰ˆ‡‰hH[vŸ žš–—–—˜šŸ£¦¬µ»ÂÃÀ¼¸¸ÀÂÀ¶¬ “‘‘’•™¡´·¶´´²´·½ÁÁ¼³¨šŽŒ“ž§®²´µ·¹º½¾½½¼»³¯°²³²¨‘‡i,0-,,+++-//.-.///0/254//...-+-/71.+*++++*+*TXZXVQRYb^[[ZYXV\YVUZ][XW[_
TÁÆÃ··ÈÈÃÉÑÏÇÄÆËÏÐÎÎÎÑÐÃ°š””—›¢«±µ¸»½½¼¼¼¾¿À¾¼º³ª¥ ˜—˜œž›š˜•”’“””’‹Š‰‰‹Ž’–˜›œŸ¡¤¦¦¦¥ œœ›™—˜–—–’ŒŒ‹‹‹ŠŠ“•“–šš—™œž¡£¦¦¨§¦§©’`2D{•Œ‡ˆˆ‰‹Œ‘Ÿ¤¤¦£ œ—”•–™ ¢§«²ºÁÅÅÀ»º¿ÂÁ»°¢“‘““—Ÿ©±¶¸¶´³³µ»À¿¸®£”– ©°´¶·¹º¼¾¾½½¾¾µ°±³µ³¯›…D62/-,--.00/..//...04511.-.--,+151-+*++*+--TUTTTQPPVZUUVVVWWUWY\ZZZ[[[)
"“ÍÈÁ´»ËÆÂËÑÌÇÇÉÎÒÒÏÍÏÓÒÆµ —”“˜ž¨®´¹»½»¼»¼¿¿À¾½»¸±«£™˜–™Ÿ ›˜——“‘’•”‘‘Ž‘““’“•™œœžš™›Ÿ£¦ª©¨¤¥¦¢Ÿžš”‘’’””•š›Ÿ££¥¨ª«®®¬««¯Ÿ‚hT>638Kd…™“‹‹‰ŠŒ‘”šŸ ›–••–˜› ¢¥¬µ»¿Åľ»¾Â½±£—‘’‘“”›¥¯µ¸·µ³³´·¼ÀÀ¾¶¬Ÿ‘Š™¡ª±´·¸¹»¼½¾¾¾¾¾µ°±³µµ³¤Š‰Y2/+-.++.00.//00/-./1420/...,+,/43.+****+-,SQRSRQPWWWRQQTVUSRTUWVSUTTW9 +
PÁÈÈ¿²¿ËÄÁËÒÌÉËÏÒ×ÒÏÍÎÑÔʸ¡—”“”˜¥²·»¼¼»»¼¿¿ÀÀ¿¾º´®§¢›˜—šœžŸž›™™˜–’’–˜œœŸ ŸŸž››ž £§ª°³¯¯©¦¢—““••““””—šœŸ£¥©¯°±²³´µµµ¶¶µ´³®®«¨¢œœ ¡•ŽŒ‹Ž‘‘“•˜˜™šš˜’’“”—𣍬³»ÁÄÄ¿¼½Âý·§™“’‘’‘”™¡¬´¸·¶¶µ´¶º¿ÂÀ¼²¦–Ž‹‘œ¤°µ·¹º¼½¾¾¾¾½¼·±±³µ·¶«‰q4.,--*).///0/.//../07600.-..,+-/44,)**+++*[Y]ZT[[^eaZ]YX[YW\XXXW[XYVSG”ÎÈȺ²ÃÈÂÂÌÒËËÏÓØØÒËÌÍÑÔË»¤—”•”•¡«±¶¹¼½½»½¾¿ÀÁÀ¾»¹³¬§¡ž›š™šœŸžžœš™™”Ž“—œ £¥§¦¥¦£¢¢££§§¤¤¦¨«¬¬§¢™‘‘““•—–™œž¢¦ª®²¶¸¸¸¹º¹º¼»¼¿¼¸·µ´°©§¤£ œ˜“Œ‹Œ‹Š‹‹Ž‘“””’ŽŽ’•˜œ¢¨®³ºÀÆÇÄÁ¾¿Âľµ«”’”’‘“–œ¥°·¸¶µµ´µ¹¾ÁÁ¾¸¯ Š‹•Ÿ§®´·¸¹º¼¾½½¼»»»¶°±³µ·¶²™Š‚E20,)++.0/./../../.0<C4/00.-,,,,05.++,++*)UVUSTWUV[[X\VXZ[]_`acac_b^YT
LÃÌËǶµÅÈÃÃËÔÐÏÕÙÚÖÏÊÊÍÒÕ;©˜””“–ž¨®´¹»¼¼¼¼½ÀÁÁÁÀ½º¶±«¤ ŸœœŸžš˜˜•”‘‘•£¥©ª««ª¨ª©ª¬¬«ªª©§¥¢ š—“‘ŽŽ”–˜š¡§¬³¸¼ÂÃÁÁÁÃÂÁÃÂÂÀ½»º¹¸¶²®«§¢—Œ‰ˆ‡†„†ˆŠ‹ŒŒ‹‰’•›¡¦¬²¹¿ÅÉÆÁ¿¿Â¿· •“””“’“˜ ©³¸¸¶´³³¶¼ÁÂÀ¼´©˜‹‡Œ˜¢¨°µ¸¸¹¹»½¼¼»º»¼¶°³´¶·¸³¤‹c10-**,.0/////....-.2<512/./-,+*-23,*,+*()MKPOPSRQNRONMQRTVTUUWWXZ[WTS%ÐËÌð¶ÆÆÃÄÏÔÔÕÖØÔÏËÇÈÍרÐÁ«™”•““›£´¸»¼½½½½ÀÀÁÁÁÀ½¹µ²ª¤¡ŸŸžŸ¡ ŸŸžœ˜—–•’‘’“—› ¤§ª«ª¨§¨ª©©ª§¥¤ ›–’ŒŽ“˜œ¢¤«°µ»¿ÂÅÈÈÇÆÅÆÄÃÂÂÁ¿¾¾½½»»¹¶³¯ª£•‰‡„„„……†ˆ†…†‰‰Š‹Ž”›¢¥¬²·¼ÂÅÆÃÀÀÄÄÀº°¢—‘“•’“”𧝶·¶´³²µºÀÃÂÀº±¤’‡…𥫱µ··¹º¼¼»º¹¹»¼´°³µ·¹º·¬–Žy80.+**///..0.---,,-.02351-/.,++-.53--+)*,MOPSOQRSQSSXQSSRSSTRPRSSTSSO>C¾ÍÍÌÀ¬¸ÉÆÃÄÌÔÖ×ÕÔÐËÈÇÉÌÖÚÒ“‘‘”ž¨°µº¼½¾¾¾¾¿ÀÁÁ¿¼¸µ¯¨¤¡ Ÿ ¡¡¡¡ ›š™™—••—˜™™šœŸ£§¨¥££¢¥¥¥¥¢Ÿ›—”ŽŒŽ•—¢¦¬°¸½¿ÂÄÅÆÅÇÇÆÅÄÂÂÂÀ¾½¼º»¼½¾»·´¯¨£œ’ˆ…„‚‚‚„„„††…‡ˆˆ‹‘—¢§«°¶¼ÁÅÇÄÀÁÄÆÂ¹±¥™“’•“‘’˜£®´·¶µ³²´¸¾ÂÅÿ¸Ÿ‘‡…‘§¯²´¶¸º¼½½º¹¹¹¼¼µ±³´·¸¹¸°š‹†M00.,,00//.-,-./-+,-/0372..--,*,-051-)***PRRWPPRSSTX[RPSRSQROLNPRQPPPNƒÐÌÌʹ¯¼ÉÆÂÃÌÔÖÖÕÒÍÇÅÆÇÍ×ÚÓÆ²š”’˜¢ª±¶»½¿¾¾¾¾ÁÁÁ¿½¹·³©¦£ ¡ ¡¡¡¢ žœœœœœžžž¡¢¢ ŸŸœ˜–“”••”’‘‘’•˜œ¡¦©®²·º¾ÁÂÃÄÄÄÄÃÂÀ¾½¾½»¹·¶µ´´¶·µ³¯ª¢™ˆ„ƒ€€ƒ„ƒ…†††ˆ‰“™¤¨«®´»ÁÅÇÆÃÁÅÇý²¦›””””‘“Ÿª´¶¶¶µ´µ¸½ÁÄÄÀ»µ¨™Š„‡” ¨¯³µ·º½¾½»º¹¹º¼¼´±³µ¸¹ºº²žŠg/31,,11//0/-,...-,,..132/.--+,,-,/3.+**)TXSTQQSTWVVXPNRTTQSSPNOPNOQNT0C»ÌÌËŵ±ÀÉÇÂÂËÓÔÕÕÐËÆÃÄÇÌ×ÛÕȶ–’”¥´¸º½½¾¿¾¿ÁÁÀ¿¾¼»·³®«§¤¤¢¢££¡ ¡ ŸŸ žž ¡¢£¢¢¡¡¢¢¢¡ ›˜–••––—˜™š ¡£¥ª¯³¶¸»¾ÀÂÃÃÃÃÁÁ¿¾¼º·¸¶³±¯®®®¬«¨¨¦¡œ–‡€€ƒ…„‚‚…„†‹‘•𤍫¯´ºÀÆÉÇÃÃÄÅÄ¿´§““””’‘—¥°µ¶¶µ´³·½ÁÃÿ¹°¡‘…ƒ‹™¤ª®´µ¹»¼½½»¹¸¸»½¼±°´µ¹º»ºµ¥‹Œ~3*1+,/.////.-,-/-----./01-,,--,,+.030*'-UXSRSRWXZVVXPOQSTRPQRQQOOQPPY@
}ËÉÌËŲ²ÆËÈÄÀÉÓÕÕÔÐÉÅÃÅÈËÖÜÕʺ¢—“Ž‹˜¡ª¯µ·»½¿¿À¿ÀÀÁÀÀ¾½º¶³¬«§¥¥¤£ žŸŸ Ÿ Ÿ¢£¤¦¦¥¤¥¤¤£¢¡Ÿ›šœœ››œ ¢£¦ªª¬°µº½ÁÁÂÂÂÃÂÂÁ¿¾½º·¶³²³°ªª«ª§¢ ›˜“‡„ƒ‚‚€~ƒ…ˆ’˜›ž¢¦ª¯´»ÁÅÉÈÄÂÆÇž¶©–’“”’• ´¶µ´´³µ»ÀÁÃÄÁ¿¸«›Š‚„‘Ÿ¨ª®³·º»¼¾¼º¸¸¸»½º±²¶¶¸ºº¹¶ªˆŽF"//-.00.-..,-./.-,+-/02/,+,*+,,./47.+,TSQQSVTRWSUUPPPQPONPQSSSTUSTYM.«ÊÊÌËÀ¯µÆÊÈÅÁÇÓÕÖÔÏÉÄÂÄÇÍÖÚÕ˼¤–”‘Ž‹Œ’¦¬´¸»¼¾¿¿¿ÁÀÀÁÁÀÁ½¹¶±¯±©¨§¦¥¢¡žžŸŸ ¢¡¢££¤¥¤¤¤¤£¤£¡¡ŸŸ¡ Ÿ¡¢¤§ª®±´·¼¿ÃÅÅÄÄÂÂÀÀ¿½º¸¶´±¬ª¨¥¤£¢£ œš˜Šˆ…ƒ‚ƒ€}}~‚†ˆ‹’–› £¥¨ª¯¶»ÂÇÉÉÅÂÅÇÄÀµ¨›—”“““Ž“žª²¶µ´³´¶º¿ÂÄÄÃÀ¼µ¥‘…„—£©«®³¸¼¾¾¾»º¹¶¸½½º¯²¶·¸¹¹¹¸®”†Ž_.,,-/.----,--.-,++,.00,*++*,,,,/42-+RPMRUSPQSTSRSPPONRSOPQRQRTQQQS)eÉÉËÌʼ·ÆËÈÄÁÅÑÕÖÔÍÇÃÃÄÇÍ×ÛÖÍ¿¨™•’ŒŒ˜¡¨°¶º½¾½¾¾ÀÀÁÁÁÁÁÁ¾º¶µ´±¬«ª©§¥£ ŸŸŸ ¢¡¡¢¢¢ ¡¢£¥¤¤¢ Ÿ ŸŸ ¢¢¥¥¨ª®±µ¹»ÀÃÄÅÆÆÅÄÃÄÂÁ¿½»·µ³±°®©¦¡žš“’‹„}|}„ƒ}~ƒ‡‘–𢤧©¬°µ»ÁÇÊÊÅÂÄÅü²¨œ”“””“’œ§°´¶´³²µ¹¾ÁÂÄÿ¼²žŒƒ‚Žœ¥«¬³¹½¿À¿»¹¸·¸½½¹¯µ··º¼¹º¹±žŠŠv$,-,../...,,,-..-*+-/.++**,-,+++.4/+SPQUTRVVSSTSTQQRQSQOQNQOQROOOQ=(œÇÇÊËÆ¹«ºÆÊÈÄÀÃÎÒÔÒÌÇÄÃÅÇËÔÚ×Ï«œ–•’Œ“›¤«²¶¹¼¾½¾ÀÀÁÁÀÂÂÁ¿»¹¸¶´´²¯¬¬ª¦¤¡ ¡¡¢¡ Ÿ¡¢££££¢¡ ¢£¡¢¡£¥©¬®²µ¸»¿ÂÄÅÇÇÈÆÅÅÄÃÂÀ½º·´²°®®«§¢›–‘Žˆ†~~……ƒ€„†‹‘•–™œ £¦¨©¬°³º¿ÆÈÉÅÂÃÅû±ªŸ–““”“ŽŽ—£³µµ²²´¹¼ÁÂÂÂÁÀ½¹®š‰ƒ‡•¢¨¬¬²¹½ÀÀ¾¼¹·¶º¾¿¸°³¶·º¼»»ºµ¤Œ…€=).////0..----+++,-.,*,,*,,*,,*,44-
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 new file mode 100644 index 0000000000..0d66f53029 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 @@ -0,0 +1,1930 @@ +ØÖÖÓÑÑÏËÈÈÈÅÅÃÃÂÅÂÁÀÁÀ½¹¶³°«¬±´³²±´¯–‘ž°¯¯±´³³´µ¹»½½½»¸¸·¹»¼½¿ÁÃÆÇÉÌÎÏÐÎÍÌËÊÊÉÊÍÌËÊÆÇÈËÊÌÌÉÈÇÆÅÄÄÅÆÇÆÆÇÇÆÅÃÄÂÃÁÁÁÁÁ¿¾¼¼¼¼½»»¸·¹¸·¶µ³²²²²²±°¯¯®®®ª©¦£¤¥¤“}vrswqnpsqqklspmjmnlknw{’™›œ›š›››™›ššœœšœžŸ¡¢¤§«¯³µ¸ºº¼¼»º¹µ²¥ž—”’”¦®´µ·¸¹º»»¼»¼¼½¾¾¾½¼º¹¸¹º»¼»½ÀÂÃÆÊËÊÊÍÍÌÌÉžºµ±©©´¶¶¶³²²³°¬©²³¯©¢¡¨±´·µµµ³›…„•¬¸º¹··³¨ ¦¯±£W9ÙÙ×ÕÒÑÏÎÉÇÇÈÅ¿¿ÁÄÂÀÀļ·´±¬«®±²°°§Ž›®¯¯°²²²µ¹»½¼¼¼º¸¸¹»½¾¾ÁÅÇÆÉÍÏÐÏÎÎÌËÌÌËËËËÌÊÇÇÉÊËÌÌÌÊÉÈÈÆÇÇÇÈÇÇÆÆÆÆÆÆÄÃÃÃÂÂÁ¿½¼¼½½¾»¹¹¹¹¹·¶¶µ´µ´²±²±¯¯®¨¤¢¥¤f96999:7464336753/01,..2344N“žœœššœœœœžž¡£¦«®°³µ¸»¼½½¼¼¹´®¨¡›•”–𤫳¶¶·¸º¼½½¾¼½½¾¾¾¿¿½»¹¹ººº»¼¾ÀÂÅÈËÌËÎÏÐÎÊÆÄ½¸µ±¬¨§¯·¶µ´²±²²°©®²³®§¥¥«°³¶¶µ´¯¨“‚ˆ¡³¶¸···³¨¡§°´²w:ÚÚØÖÓÒÐÐÍÊÇÇŽ¾½¿ÁÁÁÃÄÁ»µ²¯®ª«°°¬¥£¬“¨®¯°±°±µº½¼¼¼¼»¹¸¹º¼½¿ÁÅÈÉÌÎÐÐÎÍÍÊÊÌÍÍÌÌÌËÉÈÈÈÊËÌËËËÊÊÉÈÈÈÇÆÅÆÆÆÆÆÇÆÆÄÄÃÂÂÁ¿½¼½½½¾¼¹ºº¹¸·¶µµ´´µ´²±±¯¯¬¬§¤ªr'Rouwwvzy{zxyzyxtsvurkgce_F U¢žœ›››œœ››Ÿ ¡£¨«°±³µ¸»½¿¾¾½º·°«£›–••˜ ¨®´¶¶¹»¼½½¾¾¾¾¾¾¾¾¿¿¼»º¹¹º»¼½¿ÁÄÇÊÍÍÍÑÒÏËÉÄÁ»·´±§¨²·¶´³³³²²¯«©®²³¨¤¦°³··¶µ³®£‰–®µµ¶¸¸¸²¤ §±¶·OÙ×ÙØÔÑÐÍÎËÈÅÄÃÀ¿¼º¼½ÀÁÂĺµ²¯®®©™”ž£–£ª¯°°±°²¹½½¼»¼º¹¸¹¹»¼¿ÁÄÈÊÌÎÑÑÏÍÌÊÊËÌÌÌËÌÊÉÉÈÈÊËËÊÊËÊÊÈÉÉÈÇÆÆÆÅÆÅÄÆÃÄÄÁÁÂÂÁ¿½»¼½¾½»¼»º¹·¶¶··´´µµ´²±°°®¯®«ª¥¤@: ¬ª¬©ª¨§§©ª§©¨§©¦£¦¦£•¥£¤•;:—Ÿžœ™š››œ›œœŸ£¥§«®±´·»½¾¿À¿½»¸³¬§Ÿ™•”–ž¦°³¶¹»½¾½¾½¾¾¾¿¿¿¿¿½¼»º»º»¼¼½ÁÂÇÊÊÌÏÑÑÐÏÌȾ¹´¯¯¬§«´¶¶´µ¶³²±¯ª¨³²«¨¢©¶¹ºº·¶²¬™~‹¢°±²µ·¹¶®£ ¦±··¯oÛ×Ö×ÕÒÐÌÍÊÇÇÅÁ¿¾º¸·¹¼¿Á¿¹µ²¯®®°«—‰‘œš¡©ª®²²°³º¼¼¼»»º¹¹¸¸¹¼¿ÀÄÇÈÌÎÐÑÏÎÍËÉÊËËÍÍËËÊÉÈÈËËÊÊËËÊÉÊËËÉÇÆÈÇÇÆÄÄÅÄÄÄÃÃÂÂÁ¿½½½¾¾½»º»»¹¹¸¸¹º·µ¸·¶¶³²³°°°°®ª 5M¦¡¢¡¢¤¤¦¥£££¤¤¢ ¡ £}>Ž¡ž¥I1‘Ÿœš™™›ššš›œ›œ £¦«¯²´·º»¾¿¿¿¾½»µ®©£›˜–•›£©¯³¶·¹½¾¿¿¿¿½½¾¾¿¿À¿¾½»º¼»¼¼½¿ÁÃÇÊÌÎÏÑÑÐÎÊÆÁ¼¶±«ª¨¨³µµ´´´´³²¯©§±®«¦£´¸º¹¶´°¤‰€’§«°³¶·²£ §±¸¹²•ÛÙ×ÓÑÓÏËÊÈÄÄÃÀÁ¿¼º¶´µ¸½ÀÃÿ¹´²°¬«£ˆˆ”›Ÿ§ª«¬°±¯°º½½½½»»º¹·¸º¼ÀÀÃÆÈËÎÐÐÐÏÍÌÊËÌÌÍÌËËÊÉÇÈÊËÊÊÉÊÊËËÉÈÈÈÉÈÇÆÇÇÆÄÃÄÃÅÄÃÂÂÀ¾¾ÀÀÀ¿½»»¼¼»ºº¹¹¹¸¸¸¸¸µµµ³³³²°¯¬5T§¡£¢¤¥¦¦¦¦¥¥¥¦¤¢¢¡ ¢T1s¥Ÿ¢F)sš›—–————˜™››ž £¦ª¯³¶¸º½¿¿¿¾¾½»¹±¦Ÿ™•–›£¨®³µ·¹»½¾À¿¾¾¾½½¾¿ÀÀ¾¾¼ºº»»½¾¿ÀÂÄÈÊÍÏÏÐÑÏÌÈÄ¿º´©§¦§°µ´´³´³³³²¯¨©±±¨¢¥°¶¸ºº·²¬ƒ‰˜¤¨¬¯²µ¶²¤¤§²»º²¬ÚØØÕÐÏÎÌËÉÃÁÀ¿¿À¾º·²°²¶»¿Â¼¸´²®¨©ª„Œ”ž©©««®°±°¸»¼½½»»»¹¹¸»½¿ÂÄÆÆÊÏÑÑÑÐÍËÍÎÎÎÌËÊÈÊÊÇÆÉÊÌÌËËÊÊÉÈÇÈÈÇÇÇÇÈÉÈÅÄÄÄÆÄÃÄÂÁÀ¿¿¿ÀÀ¾¾½½½¼»¼»¹¸¹º¹··¶µ´´µ´²²°™2Z©¤¦¥¦¥¦¥¦¥¥¤¥¤¤¤£¢£‡HeP¡ ¡@&-Hh‰—–’”—ššœ ¤¦©°µº»½¿¿ÀÀ¿¿¾»¶®©¡œ˜•™¢ª®²¶·ºº¼¾¾¾½½½½»½¿¿À¿¿¾»¼»º¼½¿¿ÂÄÆÊËÎÏÐÒÒÎËÇ»·°«¨¦¥¦²³´´´´µ´²¦©¯¯¥ ¥·¹»»º·°§Š™¥©¬°²µµ´«¡¤©´½»³¯ØÕÖÕÑÌÊÊÉÇÄÁ½»¾¿¿»¸³¯¯´¹½Á¿»¶²©§£…ˆŽ—¥§©«¬®®·¼¾»¹»¼»ººº»½¿ÂÄÆÇÍÑÑÓÒÏÍÌÌËÌÍÌËËËËÊÈÈÉÉËÌÍÌÊÊÉÈÈÇÆÄÅÅÆÉÉÇÅÄÄÆÅÂÂÃÃÁÀ¿¾¾ÀÀ¿¾½¾¾½»»º¹¸ºº¹¸¸·¶¶¶µµ´³±°–0b¥¦¦§¥¥¦¦¥¥¤¥¤¤£££¨_gA‘¡š8$A,&6W†—›š›ž ¤§ª®²µ¹»½¿¿ÀÀÀ¾½¼·±©£ž™—™ž¦«¯²¶¸¼½¿¿½»»¼½»»½¿¿¿¿½½»»ººº»½ÀÃÆÉÌÍÐÑÑÒÒÏËÆÀ¹³©¨¥¤¨¬°²³³´··¶±«¤¨©¢ž«¸½»¹·³® „ƒŸ¨«®¯°µ·²§Ÿ£«´»½µ®ÐÌÎÐÒÎÊÇÆÆÅÁ¾¼¼¾¾½¹´°«¬®²·½¿½¶³®ª§¬°‰Œ” ¤¦¨ª©©ª¶¼¼º¹¹¹º»º»½¾ÁÂÅÈÊÍÐÐÒÑÎÌËËÌÌÍÍÌËËÊÈÇÆÆÈËËÊËÊÊÊÉÉÈÆÄÄÅÅÅÆÆÅÆÄÅÅÄÄÄÿ½½½¾¿ÀÁÀ¿½½½»»º¹·¹¹¹»º¹¹¸¶µ´³´³³•/l²§¦¦§§¨¨¨¨§¦¦¥¥¥¤¤—?S_8z¤”06‘}V5$.YšŸž¡¦ª¬®²µ¸¼¼¿ÁÁÂÁÀ¿½·³§¢™™Ÿ¥¬°±´¸»½ÀÁÀ¾½¾¿¿¼½¿À¿¾¾¿½º¹¹¹¹»¾ÂÄÈÌÍÑÓÓÓÓÐÍÇž·±¬¨¦¤¥ª°²²²³µ··´¯¨£§««¤Ÿ¡±¹½»·µ±ª“‰•¢ª¬¬²¶¶¯§ ¦´»¼·ÂÀÃÈÇËÊÇÅÄÁÁ¿½»»¼¼¹¶²¬«¬±´º½º´°ª¦§±¨‡‹’¡£§©¨©«´»¼º»¼ºº¹º¼½¾ÁÂÆÆÊÎÑÑÑÐÎÊÊÍÏÍÌÎÍËÊÉÉÈÄÆÉËÌËËËÊÊÊÊÉÉÇÅÅÅÄÄÄÄÅÅÄÄÅÅÅÃÀ¾¼¼¼¾¿¿¾¿¿½½»¹¹¸·¸¹¹»ºº»¸·µ´³´³´”0xµª§¥¥§¨¨¨¦§§¥¤¦¦¤¥vXzvT^ ,C¢¤¡ˆ_;.2Mu•¥¥ª®²µ·»¼½ÁÂÂÃÂÀ¿»¶¯©£¡œš›£¨®²´¸»½¾ÀÀÀ¾½¾¾½¾¿ÁÂÁ¿À¿¾»º¹º»¾ÀÃÈÊÍÐÓÕÕÔÒÎÊÅÀ¹³®¨¦¥£§¬¯±±²²µ¶¶³®¨£§«¦Ÿ›¦µ»¼º·²¢†ƒœ§¯´µ´¯¤¡§µ¼½¸°¹µ³¼ÁÄÆÈÆÃÂÀ¾¾»ºººº¶³±¬©¨©®´¹¼¶±¬¨¥§±—‰™ ¢¦©¨©«³º¼½¼»»»ºº½½¾ÀÀÄÇÉÎÑÑÐÑÎÌËÌËÌÍÍËÊÉÉÈÉÆÆÈËÌÍÍËÊËÌËËÊÉÈÉÆÅÅÃÄÅÆÅÃÄÄÄÃÁ¿¾½¿¿À¿¿¿À½½¼ºº¸¸¹ºººº»»¸¶¶´´µµº“2‚¸®«¦¤¥¦¦¦¤¤£¡£§¥¤¤qЧ¦ŒW™ƒ'LŸ›¡¡ŽjI93Cjž°µ¸»¼¾ÀÃÃÂÁÀ¿½¸³¦¡ž››ž¦«°´¶º½¾¿À¿À¿½½¾½¾ÀÂÂÁ¿¾½¼º¹»¼¿ÂÄÇÌÎÐÓÕÔÔÓÏËǼµ°ª¥¢£¤©®²³´³µ¸¸¶³®¦¢¨ª¢™š«¸¼»¸´°ª˜€…“¢ª®®¯³²µµ¬¢¡¥¬¶¼¿º²¶´²±·¼¿ÅÆÄÂÁ¾»½¼º¹¸µ²²¯«§¦¨¬¯·¹´®¬§£«®Š˜ ¢¤¨ªªª³º¼¿¾¼»»»»¼½À¿ÀÁÅÈÍÏÐÐÐÍÌËÌÌËËËËÉÊÊÉÈÇÇÇÉËÍÍËÊÌËÊÌËÉÉÉÉÅÅÅÅÆÆÅÄÄÂÃÁÀÀ¿¿ÀÀÀÀ¿¼¾¿¿¿½¼ºº»ºººººº¸¶¶µµ´´¼ˆ3ˆº°ª¦¦¦¥¦¤¢¡Ÿž£¤¤£ £Ÿ››ƒ$W šš› ¢žŠmR.T¶¶»½¾ÁÃÄÃÂÁÀÀ»¶±«£Ÿœ›ž¤«¯³¶¸»½¿ÁÀÁÀÀ¾½¾¿ÀÂÂÃÂÀ¾¼¼¼»¾¿ÁÅÇÊÍÐÒÓÓÔÔÐÍÈÿ¹±«§¤¢¢¤¨¬°³¶¶¹¹·¶³¬¤£ª§¡™¡²·¹¹¸´°¦†ƒ‡˜¤«®¯³´µ¸´«¢£§¸¾¿½²º²°¯±µº½ÂÃÃÁ¿»»»ºº¹¸³±¯¬§¥¤¥§¯³¶³¯«¦¨´ª‡”¢¥¦©«©ª±·»¾¾¾¼ºº¼»¾¿ÁÃÃÆÈÍÐÏÐÎÌËËÌÌËËÊÊÉÊÉÉÈÇÆÇÊÊÌÌÊÈÊÉÊËÌÊËÉÈÇÇÇÅÄÅÅÄÅÄÄÂÀÀ¿ÀÀÀÀÀ¿½½¿¿¾¿½º»»º¹¹¹¸¸¸··¸¶´²¼5Œ¹°®«ª©§¥£¦x𣢤£¤£¡žœœž€ ^£œœ››i‡¬¥X/˜º¹¼¿ÁÁÂÄÃÂÁÁ¾¹³®¨¢œ›¤ª¯²µ·º¼¾¿ÁÀÁÀÀ¼»¼¾¾ÀÁÀ¿¿½½½¼¼¾ÁÄÆÊËÎÐÒÔÔÔÑÎÊÆÂ¾·¯©£¢¤¥¦ª®°´·¸¸¹¸µ°«¥£¨¦œ™©´·¹¹·³ªš€‡œ¨¬°´´¶·²¨ ¤©¯ºÀÀ¼°Á´¯°°±µ»¾¿ÀÀ½»¹¸¹»º·¶³°«¨¤ ž ¤«²·µ¨¦¬¸™Œž¢¥©«ª¨®¸»¼¾¾¼¹»»º»½ÀÃÅÇÊÍÎÍÎÏÍÊÉËÍÊÌÌÉÉÊÊÊÇÇÇÈÊËËËÊÈÉÉËËÌÍËÊÉÉÇÅÄÃÅÄÃÄÄÄ¿¾¿¿À¾¾¿¿¿¾¾¾¾½»¹ºº¸¹¸¸¹¸¶¶¶¶¶µ±¹s2’·°¯¬«©¦¤¦—G#L—¥£¤¤¤¡Ÿžœt g¤›œœŸi1t¯¨³o0”¼º½¿ÀÁÁÁÁÁÀ¿º´°ª¦ œ¢©®±´·¹»¿¿ÀÁÀÁÁ¿»º»¼¼½¾¿¾½½¾¾¼»½ÁÄÆÊÍÐÓÓÕÖÒÎËÇþº²ª¥¢£¦¦¨¬°²¶¸¹¹¹¶´®ª¢ ¤ —°µ¹¹·¶°§Ž‡”¢«°³¶·¶°¤Ÿ¤¨°»ÁÁ¹±Éº±°±°²µº¼¾¿¿¼¸··¹¸¸¶µ²¯©¥žšš¡§±¸µ¬¨§¯°‘›¢¥§ª«¦¬¸¼¿¿¾½»»¹º»¼¾ÁÄÇÊÌÍÍÏÏËÉÈÈÊËËËÊÉËÊÉÈÆÈÉÉÊÌËÉÉÊÉÊËËËÊÊÊÊÈÆÆÄÄÄÄÄľ¾À¿¿¿¿ÀÀ¿¾¼¾¾¼ººº¹¹¹¶¶¹¹·µµ¶µ³±¸j.—´°¯®«¨¥¥›B!)#JŸ¤¥¥£ žŸž n"r¤£zOXw±¬²Z;¬½»½¿ÀÂÁÁÂÁ¿»·²¬§¢Ÿœž¤¬¯³··º½ÀÀÀÀÁÁÁ¼»¼¼»½½½½½½½½½¼¼¼ÀÄÇÊÍÑÔÔÕÓÏÊÉÄÀ¼µ®¥¡¡£¦§©«¯²µ¹º»¸¶³¬¥ Ÿ œ™¨¶¹¹¶´±¬‚‚Œ™¥©¬®²¶·¹´£ £«´¾À¿¸¯Ïµ²±°±´·¹º¾¿½¹¸·µ´¶¶µ³²®¨¡™—˜š£¯¹¹¯¬¶¢•££¦ªª¨¬¸¼½¾½¼½º¹»¼½¾ÁÄÇËÌÎÐÑÏÌÉÊÉÊËÊËËËÉÈÉÈÇÈÉÊËÍÌÉÊÊÉÊÉÉÉÈÈÉÉÊÉÇÅÄÃÄÄÃÁ¿¾¾¾¿ÀÁÀ¿À¿¿¾¾¼»»»ºº¹¹·¸»»·¶¶µ³²±¶b/𴝱°°©§¥M'()%dª¤¤£ ¡¡Ÿ¢j"}¥ž¤‹EŒlwµ²¥?`½½¿ÀÂÃÂÁÀ¿¼¸´©¦ žž¢¨¬²µ¸¹»¾ÁÂÁÀÀÀ¾»»½¾¼½½½½¼¼¼»»º½½ÀÃÈÌÎÒÓÔÓÐÍÈÅÿ¹±¬¥¡ £¥¦©¬°´¶¹º¹¹¶´¯¨Ÿœœ˜°¹»¹·´¯¥Œ‚ˆ‘ž¤©¬¯´¹¸¸´©¢Ÿ¤®·¾Á¾¹°ÏȺµ´²´µµµ¸¹»»º¸¶µ´³³µ²³°«¦ ™–““›¡¨µ»·³¶¶› ¢¥¨©¦ªµº¼¾½»»º¹º½¿ÂÃÅÉÍÎÎÐÐÎÌÊÊÊÉÉËÌÍÍËÉÊÈÈÈÇÉËÌËËËËÌËÊÉÈÈÉÊÈÈÉÇÆÃÁÁÂÁÁ¾½¾¾¿ÀÁÁÁÁÀÀ¾½¼»¹¸¸¹¹¹º»ºº·µ¶µ³³³·^4³®°°°¯ª¬u%&&'&/•¥£¤££ Ÿ¥e'…¥¦•IEzXy´·|5•ļ¿ÀÂÃÃÃÂÀ¾º´®©¤¡ŸŸ¢§¬²¶¸»¼½ÀÃÄÂÀÀ¿½º»½¾¾¿¾½½¼¼¼¼»»½¿¿ÃÊÎÐÓÓÒÐÍÈÅÃÀº³®¨¥¡¡£¤¥©¬±¸º»»¹¹¶³¥›š™£³º»¹·³«œ‚‚‹–¢¦¨±¶·¹¸±¤¢Ÿ¥°¹¿Á¿»°ÐÍÀ·¶¶¶´µ¶¶·¸¹··µ´µ³²²³´³°«¥Ž’–›¥³½¿¾¹¦› £¤§§©´¹»¼¾¼»¼¼»¼¿ÃÄÅÊÍÎÏÐÐÏÍËËÊÉÉËÍÎÍËÊÊÈÈÈÈÇÈÉÊÌÌËÌËËËÊÈÉÉÈÇÇÇÅÄÂÂÁÁÀ¾¾¾ÀÁÀÀ¿¿ÀÀÁÀÀ¿¼¹¹ººººº»º¸¶´¶µ´´´¸[9¡±®¯¯°®«¥B%)'&'++y¬¤¥¤£¢Ÿ¥^-§¦dm…g@}¸µPJ¹ÁÀÁÂÃÃÃÄÂÁ½µ°ª¦¢¡Ÿ¡¥ª®´·º¼¾ÀÃÄÄÃÁÁ¿¼»¼¾¿¿ÀÀ¾½½¾½½½»»½ÁÅËÏÑÔÔÑÏÊÆÃ¿»´°«¥¢¡¢¤§§¨«²·ºººº¸µ²«£›™—™©¶¹º¸³¬¦“…™¤§ª¯³¶¸º·¯¦¡¢©³º¿ÀÀ»¯ÑÏĸ··¸·¶µ´´¶¶µ³³³²³³²²²´³¯¦¢”‰‰Š‘•ž½Æ¿«˜ž¡£¥¤§²·º»½¾¼½½¼½¿ÂÅÇÉÌÍÏÐÑÎËËÊÉÊÊËÌÌÊÊÊÊÊËÊÊÉÈÉËÍÍËËÌËÌËÉÈÉÊÈÈÇÅÄÂÂÁÁÁÀ¿¿ÂÃÂÂÁÁÁ¿ÁÁÁ¿»¹º¼»»º¹¹·µ´¶·¶´´´¶X>£¯®®®¯¯Ÿ9,+)))0.v¬¦§§¥£¢§Y2–¨¢—®±µq‚½Ÿ8}ÇÂÁÂÃÄÄÄÃÁ½¸±©¦¢ Ÿ¥©¬°³·¹»¿ÃÃÅÃÂÀ¾½»»½¾¿ÁÀ¿¾¼¾¿¿½½¼¾¿ÄÉÍÏÒÓÒÐÎÈÄÁ¼¶±¬§£ ¡¤¦§§©ª²·¸º¼¹·´¯ª¢™–“ž±ºº¹µ±«œ„‚‡’ž¦§¬²¶¶¸¸µ¬¤œ£µ¼ÀÀ¾¹°ÒÒʺ¹¸»¹·´³³³²²¯¯±³³´³±°±³²¬§ –Іˆ‰“•Ÿ´«—™ ££¢¥¯¶¹»½½½½¾¿ÀÀÁÅÈÉÊÌÎÎÏÌËÊËÊËËËÊÊÈÉÈÉÊËÌÊÈÇÉËÍÎÍËÌËËÊÉÇÇÉÉÈÇÅÅÅÃÁÁÂÀ¾¿ÂÄÃÃÂÁÀÁÁÁÁ¼»ºº»½»¹¸¸µµ···¶´µ¶µQB©¯«¬¬ªZ04*+57F—§¦§¦¥¥¤§T6ª®³”›½lA®ÃÀÀÁÂÃÃÂÁ¾¹´¨¤¢ Ÿ¢¦«¯²´¶¹¼¿ÂÄÄÃÀ¿¼¼»¼½½¾¾½½¼»¼½¿½¼½ÀÄÇÌÏÑÑÐÏÎËÈþ¹³®©¥¡ ¢¤¦¦§ª³·º»º¸¶²®¨¡™“•§¶»»¹³¨’€†‹š¢§«°µ··¹¸²§š¢°¹¾¿À½¸®ÏÒξ¸º»º¶µ´³²³±¬®±±³²°¯®®±¯¬©Ÿ‘†„‡ŠŒ•›—–ž££¢£¶º»¾¼¼¼¼¿ÀÀÂÄÈÊËËÍÎÎÌÉÇÉÊÉÊÉÊÊÉÉÊÊÉÊÌÌÈÈÉÊÌËÍÍËÊËÊÈÈÈÇÉÈÆÅÅÄÂÂÁÂÁ¿ÀÀÃÃÃÂÁÁÀ¿¾¿¿¿¾½½½¼ºº¹··¸¹¹¸¶¶µ²LI®®¬¬ª®œme=1bm¥£¦©¨¦¤¤¦O<¤¯¯¯±²³·ªAjÆ¿ÀÀÁÂÂÂÁ¿¼·±«§¢¡ŸŸ¡§°³¶¸º½ÀÁÂÂÀ½½»ºº¼¾¾½»º¹ºº»»¼½½¾ÂÅÉÌÏÑÒÏÍÍÊǽ¶¯ª¦¢ ¡¢¤¨¨¨«°µ¹º»º¸µ±¬¦ ˜“¯¸¸¸µ±©Ÿ…‚Š–Ÿ¦©±¶··¸µ©Ÿ˜ž¦°·¾Á¾·¬ÏÐÏź¼»ºº·¶³²²°«©ª®®¯®«¬®°®¬§‹‚ƒ…„‡”–‘œž¡ ¡¨³¸»¼»¼½½¿¿¿ÁÅÇËÍÎÎÍÍÊÊÈÉÉÊËÊÉÉÊËÊÊÊÊÌÌÊÉÉÊËËÌÍÌÊÊÊÉÈÈÈÉÈÆÃÂÁÁÁÁÁÀÀÁÀÂÁÁÁÀÁÁ¿¾¿ÀÁ¿½½½½»»»º¹·¹¹¹·µ²¯FO°®®°¯¯¬°³¤H7–£ ¥¥§§¥£¦ªLB«±±°±²³µ½‡:›Ä¾ÂÃÂÃÄþºµ°ª¦¢¡ ¢¦«°³¶·º¼¿ÀÀÁ¿½¼¼¹·¹»»º»¸·¶¸¹º»»¼½¿ÄÇÊÌÎÐÑÐÍËÈÄÀ¸±¨£ŸŸ¡¢¥¨§¨¬´¸ºº¼»¸µ±¬¦ž””¦µ¹·µ²¬¤“‚…–¡¨¬¯µ···µ®¤œš ¨®¹¾Â¼µ«ÎÒÐȼ¼»ºº¹·¶µ³®©¤¢¤©«««ªªª¯¯ª£—‡€‚ƒƒ‡Œ‘Œ”›œž¨²·»½¼»»½¿¿ÀÂÅÇËÎÐÐÏÍËËËËÊÊÊÉÉÉÉËÊÉÈÊÍÍËÉÊÊÉÊÌÍÌÌÉÉÉÈÈÇÈÉÅÃÂÁÁÁÁÀ¿¿¿À¿¿¿¿ÀÁÁÀ¿¿¿¾½¼½½½¼¼¼»¼·µµ´³²±§;Q²¬®¯¬¬¬®ˆ>8}§ £¤¦¨¥¥§ª®IC¯²²±²´¶¸¸XV½À¿ÂÂÃÃÅÂÀ¼µ°¨¥¢¢¡¤ª±´·¹¼¾¿¿ÀÀ¿¾¾»ººº»ºº¹¸¹··¹ºº¹»»¾ÂÆÊÍÏÑÐÐÍÉÄÁ»µ±«§¡žž ¡£¦§¨¬³¶¹¹»¹¸¶²«¤ž–¬¶¸¶³°¨ž‡…†š ¦¬´¹¹·¶³¬ ššŸ©±º¿À¿¼´ªÏÏÏÊ¿»¼»¹¸¹·µ²© Ÿ £§©¨¨¨«¬®°±¬§ ’‚}|€„‰ˆ™™™›§²·»½½½¼½¿ÀÂÃÄÇÈÊÍÑÏÌÌÌÌÌÍÍÌÊÉÉÊÉÉÉÆÈÊËËÈÊÊÊËËÍÌÌËÊÉÈÈÈÉÈÆÄÃÂÂÁÂÁÀ¿¿¾½¾¾¿¿ÁÁÀ¿¿¿¾½½½¿¿¿À¾½¼¸·¶´²±±¥7W¶®¯°±¯®®¯—|z¦¢£¥¦§¥¨«®¯GJ³²³³µ¶¶¼›;†ÈÀÂÃÅÄÃÿ»²¬¨¥¤£¢¤©¬¯´·¹º½¾¾¿À¿¾¿¿¼¼»¼¼¼º¹¸¹¹¹º»¼ºº½¿ÃÇÊÍÐÑÑÎËÅÁ½µ°¨¥ Ÿ ¢¢¢¤¦©®´¶¹ººº¸µ°©£›¡´¸¹µ±¥‘„Д𠦱·¸¹¸¶±¦ž››¡«³¼¿¿¾¼´¨ÎÍÍ˺¼¼¼¸¶µµ²¬¨¢ Ÿ ¡¡ ¤¨«®°®¬© {|~€†Œˆ‰–™–—¢°¶¹¼¼¼¼¼½¿ÁÃÄÆÈÊËÍÏÍËÌÌÌÍËËËÈÈÈÈÇÈÇÉÊÉÉÉÊÉÉÊÌÍËÊËÊÈÉÊÊÊÉÈÄÄÃÃÃÃÂÀÀÀÁÀÁÁ¿¾ÀÀÁÀ¾ÀÁÀ¿¿¾¾¿À¾¾¼¹¹¸µ´´µ¤5c¹®°±²¯®°°°²µ²ª¥¥¦§©¨§©¬±°EP¶²²³¶¶·¿oE³ÅÂÃÄÆÆÅľ·¯ª¥£¡£¥¦«°²µ·¹¼¾¿ÀÁ¿¾¿¾¾»»»»»»¹¸¹º¹º½¾½¾ÀÂÅÇÈËÎÑÒÏÌÉÄ¿º±¬©¦¤ ŸŸ¡¢¢£¤©¯³·¸¸¸·µ³§¡™˜«´¶´²®¦œƒŒ—¤«·¸¹·¸´ª¢™šž¥®¹¾¿¾¾¹²¨ÑÐÒ̹¼¼¼¹·µ´°«©¥¡¡—•——›ž£¦ª®°±¬¢‹~zz|}€ˆ‡…“”““Ÿ®µ¹º»½½¼»¼¿ÁÂÅÈÊÌÍÍÌËÌÌÌÌËÌËÈÇÈÇÆÈÇÉÊÊÊÊÉÈÉÈÉÊÊÉÉÉÉÉÉÊÊÉÈÇÄÄÄÄÃÂÀ¿ÀÁÂÀ¿¿¾ÀÀ¿¾¿¿ÀÀÁ¿½¾¿¾½¾»¹¹¸¶´µ¶ 6p¼ªª³²¯¬°°°²¯©§¨©©©¨¦§«²¯AWº³²´µµº²DlÇÁÂÃÅÇÆÅÃÀ»´¯ª¦¥¤¤§©±´¶¸¹¼¾¿ÀÀ¿¾¿¾»¸¹º»»ººº»¼¼¼¿¿¿ÂÆÇÈÉÉÌÐÔÒÏÌÇÁ»µ°«§£¡Ÿž ¢¢¦«°³µ·¸¸µ´²§ šž®²²±±©¢{‚„Œ”¦²·¸¸¸¶±§žšœŸ¨µ½¿¿½»¸±¦×ÔÓÏȼ¼»··¶´²°¬©¥¢¡œ”Ž”™œ¡£¦©¯°¯® ‹yyy{|~„’ž¬±µ¹º½½¼¼¾¿ÀÂÅÈÊÏÏÎÍËËËËÌÌÍËÉÈÇÆÆÆÈÉÉÉÉÉÉÊÉÈÉËÌËÊÉÉÉÉÉÊÉÇÇÆÇÆÅÅÃÁÀ¿ÁÀÀ¿¾¿ÁÁÀÀÀÀÀ¿À¿¿ÀÀ¿¿¿º¹¸¸·¶¶¹š2yÁ™m¹³²}£±¯°°°®«¬«¬ª©¨§ª¯³<^¸²²µ¶¶Â8¢ÈÄÄÆÇÇÅÃÁ½¶°¬©§§§§«¯²¶¹ºº¼¿ÀÀÀ¾¾½¾¼»¹º»»»»¼¼½¾¾¿ÂÁÂÅÇÈÊÌÍÐÑÒÐËǽ·±®«¨¤¡ŸŸ Ÿ ¢¤§¬°³´··¶´²°¬§¡œ¥®°°°¬¥™…€ƒ…Ž˜¢®¶¹¸¸ºµ¬¤œœ¡ª¸¿¿À¿»¶®¥ÚÖÓÐʽº¼ºµ´²°®©¦£¢œ•Љ‹‹“•–ž £§©¬ª§œ†{yz{z~|‡Žª¯´¹¹¼¼½¾¾ÀÁÁÅÊÍÐÒÑÎÌËËÊÊÊÉÉÇÇÆÅÆÇÇÉÈÉÉÈÉÊÉÉÊËÌËÊÉÉÈÈÉÉÈÇÆÆÆÆÆÅÂÁÁÀÀ¿¿À¿ÀÁÀÀÀ¿ÁÀÀÀ¿¿¿ÀÂÁ¿»º¹º¹¸¸¼”.|Á²Ro‚wc²±±²²°®¯¯®®«¬©§®³¶ª;c¶±³·¹½Á]ZÈÅÆÇÈÇÆÂÀ¾º³©©¨§§«®²¶·¼»¼¿ÁÀÀ¿¾¾½¼»»»»¼¼¼¼¼½¾½¾ÂÄÃÃÅÇÉËÌÎÎÏÏÌÇÁ½·±®«©§¢ž ¥¥¨±´´´¶µ³²¯«¨¡¦®®¬¦¢€„†Œ•Ÿ©²¸¹¹¹·¯¥Ÿœ›œ¤®¹¾ÀÀÀ»´«¤ÜÙÓÎÈÀ¹º¹·´±¯«©¦¤¤ ˜‹ˆ‡ŠŽ“——›Ÿ¢¡¤¢•}yyyxywŠŒŽ™©¯´·¸¹º½¿ÀÂÂÂÆÉÍÐÑÑÏÎÌËÌÊÉÉÈÇÆÆÇÇÈÇÉÈÉÉÊÊÊÉÉÊÌÌËÊÊÊÉÉÉÉÈÇÅÅÅÆÆÅÂÁÂÂÁÁÁÁÂÂÂÁÀÀÂÂÁ¿¿¿ÀÁÁÁÁ¿»»»»ºº¸½“.ƒ¿¼iPsBй²²³²±°±±¯««©«±³µ¦7f¸²¶¹¼Ä¨A“ÌÄÆÆÆÅÄÁ¾º¶°©§§¨©ª¯±´µ·»»¼¾¾¿Á¿¾¾¼»ºº¹»¼¼¼»º»¼½¾ÁÃÃÄÆÇÈÊÊÌÍÎÎËÆÀ»µ°¬©§£ œ›œž¢¥¨¬¯²³³³´³³±«¦¡Ÿ§«¬©£™…€ƒŠ‘›¦¯µ¹¹¸·°¨¡œš›ž§³»ÀÀ¿¾»´ª£àÝÖÍÆÀ¹¶µµ³¯«ª§¥¤¥¢˜††…ˆ‰ˆŽ’˜—Œ|yyxvsz†ˆ–§®±µ¶¸¼½¾ÁÃÄÅÇÊÏÒÐÏÏÎÍÎÎÌËÌÌËÊÇÇÉÉÈÈÇÇÉÉÉÉÈÇÉÊËÊÊÊÉÉÉÉÉÈÅÅÆÅÄÄÄÁÁÁÁÁÁÁÂÂÃÃÂÂÃÃÃÂÁ¿¿¿À¿¿½¼»¼¼¼»»¸¾Ž/ˆ»¸dž_ª³³²±¯°±²³²¯««®²³µ¡2h¸²·½¾ÄpRÀÈÅÃÅÆÃÁ¾¾º³®©¦§¨ª±²µ¶·º½¾¾½¼½¼¼¼ºº¹¹¹º»º¹º»º½¿ÀÂÃÄÅÆÉÊÊËËÍÍÊÇý·±¯¬¨¥¢ž›››œŸ£¦ª®°±±²²²±²±®«¦Ÿ §©ª«¥Ÿ‚…Œ”¡ª²·¹¸¸³¬§Ÿœ›œ¡ª·½À¿½¼¹±©£ÚÙÔËĽ¸¶¶´²°®¬«¦¢ ¤¤ 𓉄ƒ…ˆˆ‡‡‡‚€~‡‹Ž„{yxrlxƒ„Š”¦¬°´º¼¼¾ÀÂÃÆÉÌÏÒÒÐÏÏÏÎÎÍÍÍÌÌÊÈÇÆÇÈÈÇÆÇÉÉÈÇÇÈÈÈÉÊÊÉÈÉÈÉÈÇÆÆÆÄÄÃÀÀÁÁÁÁÁÂÂÃÄÃÄÄÅÄÄÂÀ¿¿ÀÀ¼»»»»»»¹¹·»Š.ޏµªQa~·±²²°®¯°²³³²°°¯²´´±˜0l¸´º¾Â°EˆÍÄÅÃÅÆÃÀ¾»·²ª©©ª¬¯²µ·¹»¼½½¾¾¼»»º»»º»¹º¼¼½»»½¼¾ÁÃÄÅÆÇÈÊËÌËÌÌÊÇÿ¸³©§¦¤ šš›œ¡¥¦ª®¯°²³²±¯°¯©§Ÿ §¨¨¨¡˜…ƒ‰’œ¤®¶¸·¸¶°«¥ž››ž¦°¸¾ÁÁ½¼·¯¦£ÉÉž»¸¶·´±¯ª¦¡¡¢¢¡œ–Œƒ€ƒ…††„€{yxwx~€|vvtlv„‚†”¤««°µº¼½¾¿ÀÂÆËÏÐÒÓÑÏÎÏÎÎÎÍÌÊÉÉÈÈÆÆÇÇÇÈÉÉÉÈÇÈÉÊÉÉÊÈÈÈÇÈÇÆÆÆÆÆÄÅÁÀÀÁÁÁÁÁÃÃÃÃÃÃÅÄÅÄÁ¿¿½¾¾»»»»»»º¹º·¼ˆ-¹¯µ_@¨µ³²²°¯±±²³³³³´µµµ²²+r¸¸¾¾ÇzL¸ÅÃÄÅÅÄÿ¼¸µ²®ª«¬¬¯²µ¹º¼½½¿¿¿¿¿¾¾¼º»½½¼¾¿¿¿½½½¾¿ÂÄÆÇÈÉÊËÌËËËËÉÅÁ¼µ¯©¨¤¢Ÿ›˜™ž¡£¥¨¬¯±±±²°¯«©¤ž ¤§§¤Œ‡Œ˜¡«µ¸···´°© š™› ©²º¾¿¿¿¼´ª¤£¼»¹¹¹¼»µµ²°®¬¬ª¦¢¢ žš•†€€ƒ…ƒ€{wtstwvwvtrojq‚ƒ‚¡ªª®´·»¾ÀÀ¿ÂÅÊÎÐÑÑÎÏÎÍÍÍÌÌÌËÉÈÇÆÇÅÆÈÈÉËÊÊÉÇÈÊËÌÊËËÊÉÇÆÅÅÅÅÅÅÅÄÁ¿¾ÀÁÁÁÂÂÁÁÁÁÂÀÁÂÁ¿¾¾½¼½½¼»¹¹»º¹¸¶¹‚-髆t³®®°¯¯°¯±²³³²´¶´³²´Š,~À»¿ÀµItÇÁÅÆÇÆÃÀ¼º¹´°¬ªª«±´¸¹¼¿¿¿ÁÂÁÀ¿ÁÀ¿½½¿¾¿ÁÀÀ¿¾¾¾ÀÂÅÅÇÉÊËËÌÌËÉÈÇÆÄ¾¹±«©¥¢Ÿœš˜šŸ¡¤¦¦©¬®®¯±±°°®¬¬«£ž¢¦¤ –†€ƒ‰“œ¦°µ¸µ·¶³ª¢œšš¥·¼¿¿¿½º±¥££³±³³µ·º·°°¯««©¦¡ Ÿœš˜—”Œ€‚}ytrrrrqoqpmifm‚‚‹Ÿ¬©«±·¼¾¿ÀÁÄÅÈËÎÐÏÏÎÌËÌËÊÊËÉÆÅÆÇÆÆÇÈÈÈÊÊÉÈÈÈÉÊËÌËËÊÊÈÆÇÇÆÅÅÅÇÅÂÀ½¾ÁÂÁÂÂÁÀÂÁÁÀÁÁ¿¾¼½½¼½½»º¹¹»¹¸¶³´’.W§«©¬±°®°°®¯±²³´²±±±³²²¼‚2–Á»½Å–:£ÈÅÆÅÅÅþ»¹¸´°ª©ª±µ¹¼¾ÁÁÂÃÂÃÄÄÄÄÃÃÃÁÀÁÂÁÀ¾¿ÀÁÄÅÇÆÇÈÊÌËËËÊÇÆÄ¿¹²¬¨§¤¡š˜™›œŸ¢£§ª®®®¯²²°°®««§£››Ÿ¢¡™‚‡™¡¬´¶¶¸¶³¤žœ›ž¨²¹½ÀÀ¿¼·®¤¢¢®®¯°²µ¸·±«ª©©¥Ÿœžœ˜““„}€}vtrppnkkmkihch{€ƒŠ›«««¯´¹¼¿ÀÃÅÇÊËÍÏÐÏÌÍÌÊÉÉÈÉÈÅÅÅÄÅÇÈÈÇÈÊËÉÇÇÈÉÉÊËÌËÊÉÇÉÈÈÈÇÅÆÆÅÂÁ¿¿ÁÂÂÁÁÁÁÁÂÃÁÁ¾½½¼½½¾¼»ºº¸º»¹¸¶´²¯T'BWcltz‡”› ¥§©©®¯°¯¬¯®® RI³À½½ÆmLÀÅÇÆÅÄÿ¼º·µ²®«ª¯²¶»¾ÁÄÄÅÇÅÅÇÇÆÇÇÆÅÃÁÂÅÃÁÁÂÂÄÅÅÆÆÈÇÉÊËÊÉÈÅÅÁ¾º´®¨¥¦¢Ÿš™˜šœ £¤¨¬®¯°¯®°±°°®¬©§¢™œ ¡›ƒ…‡’¦²·¶·¶³®¦¡ž››¢¬´»¾ÀÀ¾º´©¢¢¥®®®®¯²µ¸´«ªª¨¥ ™š››˜“‡€}~~|zxuromkjihggeaev~‚Š›¥§ª±µº¿ÀÃÅÉÌÎÐÑÑÎÌÎÍËÊÉÉÈÇÅÄÄÄÄÆÇÉÈÆÉÈÆÆÇÈËËËÊËÌËÊÈÊÉÉÉÈÇÇÇÇÃÂÁÁÂÂÂÂÁÀÀÂÂÂÀ¿¼½½½¾¿¿¾ººº·ººº»¹µ²µ¦eD;7630*0348;<?DEKPROLQSSDE‘ÃÀ¿ÁµIwËÅÆÅÅÄÃÁ¾»¹¶´±¬¬¯³¶º¿ÂÅÆÇÇÈÇÇÆÆÆÇÈÇÅÄÃÂÄÃÁÂÅÅÆÇÈÇÇÈÇÉÉÊÈÇÄÂÀ¾¹´¯¬§£¡Ÿ›š™™››ž¢¤¦«®®®¯°¯®®©¥ ™›–Š~ƒ…Œ—¢®³¶¶¶µ°¦¢ ›šŸ©²·¾À¾¾»¶°§¡¢¨¯¯®®®°±¶¶©¨§¥¢žš˜˜–•––‘Œˆ€|}}y{ywspmjiigefb\`r{€ˆ˜¤§§«¯²¸½ÁÄÆÈÌÎÏÑÑÏÍÍÎÎÌÊÈÈÄÃÄÃÃÄÆÇÈÇÅÈÈÇÇÇÈËËÊÊÌÌËËÊÊÉÉÇÇÇÉÈÉÆÄÂÅÄÃÃÃÃÁÁÂÁÀ¾½»¼¼¼»¼½½ºº¹¸º¹º»¹·µ²³±¤™’Ž‚`=243.6IOPOSWY]^ehy¦ÆÁ¿Ç@¥ÉÃÅÄÄÄÿ¼¹¶³±¯¬¯³µ»ÀÄÆÉÉÈÉÉÈÇÇÆÆÈÈÇÅÄÄÃÃÃÂÃÆÇÈÉÉÉÉÈÈÉÊÊÈÇÁ¿½ºµ±ª§¦¤ œšš››››ž¢¤§¬®¬«¬¯°°¯¬«©¥ž˜š›˜“ƒ~†œ©±µµ¶·²¨¢Ÿž›š£¶¼¿À¾½¹³¬¨£¢©µ±¯¯®®¯²·²¨¥£¡Ÿš˜——“‘““Žˆ~|{{zwsomjihggcZV\nw‡•£§¨©¬¯µ»ÀÅÇÌÎÏÐÐÎÍÍÌÌÍÊÉÆÄÂÂÂÀÂÃÄÆÆÆÇÈÈÇÆÇÉÊÊÊÊÊËÉËËËÊÊÉÉÈÉÈÇÆÄÅÆÅÅÅÅÅÃÂÁ¿¾¼¼»»¼½¾¾¾½»ºººº¹ºº¹¸¶¶´´µ·µ·º»´šuT?2>X‚Ÿ®¶¸··¸º¾ÃÇÂÂÃÂÅdZÇÇÆÆÅÿ¼º¸³¯¯¬ª«°´¸ÀÆÈÉÊÊÊÊÊÉÈÇÉÉÊÊÈÆÅÅÅÅÄÄÅÇÇÇÉÉÊÊÊÊÊÉÇÅÃÀ¾º·³¯«¦¤£¡œšššž ¡¢¤¨®¬«®±²±®«©§¥Ÿ•˜˜—‡}€„Œ•£®´µµ·µ¦£ œ™› «³¹¾¿¾½¼¹²ª§¢¥ª¹·³¯¬®®±·®¢Ÿš˜––”’‘’“‘†|{{zurplkifde`XQVlw}„‘ ¦§§©¬²¸¿ÆÊÍÐÐÐÎÌËËÌÊÈÆÄÃÂÀÁÁÀÀÂÃÄÅÇÇÆÇÆÆÇÉÊÉÉÊÊÉÉÈÉÊÊÌÌËÊÊÉÈÅÃÄÅÅÆÅÆÅÄÂÁ¿¼½¼ºº»»¼¿¿½ººººº¹¹¹··¶·µ³´³²³³³´¶·®•nO::GdЧ¶¸¹¹º»½ÀÂÂÆ¯EŠÏÆÆÆÄÁ¿¿¼¹·²¯««®±¶»ÂÆÈÉÉÉÉÉÊËÉÊËÊËÉÇÅÇÅÆÅÄÆÇÈÇÇÇÈÉÊÊÉÉÇÆÃÁ¿¼¹¶²®«¦¡Ÿ›™˜˜šœŸ£¤£¤«¯¯¬«¬¯°²±¬ª©¨£›“˜—’}…ˆ‘œ¨³¶³·¶²¬¨£žš˜ž¦°¶»¿À¾¼»·±¬§¥§¬»º·²¯°®¬«°¶§ ›š—”•”‘Œ‘’“Žz{zxuromlgdaa]XRPgv{ŽŸ¤¥¤¤¦¬°¸ÀÇÍÏÍÍËÉÈÈÇÆÆÃÀÀÀÁÀÁ¿ÀÁÁÃÄÅÅÆÇÆÈÈÊÈÇÇÈÊÉÊÉÈÉÉËÌÊÊÌËÉÅÄÆÆÆÆÅÅÄÂÂÁ¾¼¼»¼»º»»½½¼»¼º¹»¹¹¸¶µ¶µµµµ´´´´´µ¶¶¸»¼¯—wP;2;X{˜¯¼ÃÂÂÁ¾É{H¶ËÈÇÆÄÃÀ½º¶²²±¯®®³µºÀÄÇÈÉÊÉÈÈÉÉÉÊÊÊÊÈÆÅÆÇÇÇÈÇÉÈÇÆÆÈÉÉÉÈÇÆÄÂÀ½¸¶µ±®ª¥¡˜–——šœž¡¥¤§«¬©«®°±±¯«««ª¨¢˜“•”…|‚†˜¤´³µ¸¶°¨¤š™›¤¬³¹¾ÀÀ½»ºµ¯«¥£¨¯»¹ººµ°¬©§§¯²¢›–““’’ŒŒ‹Ž‘“‘Œƒ|xwuroojfca`\XQK^sx}ŠŸ¡¤£¢¤¤¥¬²¹ÁÅÅÄÃÀÀÀÁÁÁÀ¿½½¿¿¿¾¾¿ÀÁÂÂÀÃÄÅÈÈÈÇÇÆÇÈÈÉÉÉÈÉËÊÊÊËÌËÈÆÆÈÈÆÄÅÄÃÂÁ¿¾¾½½½»º»½¾¼¼½»¹¹¹·µ´³µµµµµ¶µ´µ¶¶··¸··º¿À· bJ:<GZoŒ£®·œC}ÐÈÈÆÅÅ¿º·²±²³²°¯³¸½ÃÇÈÉÈÉÈÈÈÇÇÆÈÈÈÈÇÆÆÇÈÇÆÈÇÇÇÈÇÇÇÇÈÇÇÆÄ¿¼¹¸²°«¨¤Ÿ›˜––—›› ¤¦©¬¨ª¬¯¯±±«««ª¨ –”’Œz~„Š”ª³³³µµ°©£™–𣩱¸¾¿¿½¼»¹³¬©¥¡©²¿º·¶³®«§¦¥¥¯°—”‘ŽŒ‹Š‹Š‘‰~yurqqnjeb`]WRLKYouz‹Ÿ¡¢£¢¢¡£¥©¯´·»¼¸·¸¹º»»½¼º¼¼¼¼¾¿¿ÀÁÁÁ¿ÀÂÂÆÈÇÆÈÆÇÈÈÈÉÉÈÉÌËËÌËÌÌÉÇÅÇÈÈÇÇÆÅÄÂÁÁÀ¿½½¼¹»»¹ºº¹···¸·µ²²²²µ·¸¸µµ¶·¶¶··¸·º¼¼¾Á¿³˜ƒoYIABGVLlÅÎÉÇÆÄ¿»¸µ´´µ³²®¯µ»ÁÅÉÊÊÉÉÈÈÈÈÇÅÆÇÇÇÈÈÉÈÇÇÇÇÇÇÈÇÇÈÉÈÇÆÅÂÀÀ¼¸¶³±«¨§¦£Ÿ™——™š›œž£¦ª¬«¬®¯¬«ªª¨¦Ÿ–“Ž}zˆ‘™¡¬²³·´°¨¡ ›˜˜Ÿ¦®µº¼½¾½º¹µ±¬¨¤¥®¸¾»·®©¨§¦Ÿ ¤¯ªš•’‘‹Š‰‹Š‹‹ŒŒ†€{vqoomida^[SLIGWqtw‡ ¤¢¡ ¡£¨ª«±±°°°°²µ·¹¸¹»º»¼¾¾ÀÁÁÂÁÁÂÂÃÅÆÇÈÆÆÉÉÉÉÈÉÈÈËËËÌÍÌËÊÊÊÈÈÈÇÈÇÆÅÃÂÂÂÁ¾¼»ºººº¹º¸¶µµµ¶´´´µ·¹º»»¸¸¹¹¸·¸¸¸¹¼¼¼¼¼¾ÀÂÅÉǼ³ž„v|™ÆÎÊÊÈÆÄÀ»·±±µ¶¶µ²®±¸½ÄÈÊÊÌÉÇÅÅÅÃÄÃÂÄÅÄÄÄÆÆÆÇÈÇÈÈÈÇÈÉÈÈÅÿ¿¼¸¶²°¨§¦¤¡ ™—™›œœž¡¦©¬¬®®°®¬¬ªª©¦¥¡“„wz€‹”ž§±µ¶³«¢Ÿ›™šŸ¤«²¶¸¼¾½¼»¸³¯©£¡¨²¹¸º¹¯¢ ž•—›œŸ¯™•““ŒŒ‹ˆ‰ˆ‡…‚{xtonliea]ZOIDCOptwƒœ££¢ŸžŸ ¡¢¢¨«¬¬¯²³µ¸¹¸¸»½½ÀÁÁÂÂÂÂÃÃÄÄÅÇÇÇÆÈÉÉÉÉÈÈÉÊÍÍÎÍÊÊÌÌËÉÈÈÈÈÈÈÆÅÆÅÃÂÁ¿¾¾½½»¹·¶µµµµ·¸¸»½¾À¿¾¼»¼»ººººº»½½¾½¾¾¿ÀÂÃÂÁÅÇÇÄÈÍËÊËÉÈÆÁºµ°°°±´µµ³³¶»ÀÆÈÉÉÇÄÃÁ¿¿½»º»½¾¿¿¿ÁÂÃÆÅÆÆÇÇÆÅÅÅÄÂÂÁ¿¿»·µ±ª§¥¤¢ Ÿ›˜—™›› ¢¢§ª¬®¯¯°°¯¬«©¨§¤“ŒywzŽ˜¤¬¯²µ´¯§ œ››¤ª¯³·º¿¿¾¼»¹´¬¦¡£«´¸³´³³©–’””’•°ž“”‘Œ‹‰‡‡ˆ†„~{xtqolhec]UMHA@Iluxƒœ¤¢¡ž››ž ¢¡Ÿ ¥¨ª««¬®°³µ´´¶·¸º»½¿¿ÀÁÂÃÃÃÄÃÄÅÅÇÆÇÈÉÈÊÊÊÊËÍÍÎÌËËËÌÌÊÉÉÈÈÈÇÇÇÇÆÆÆÇÃÀÁÀ¿»¸¹·¶µ·º½ÀÁÂÃÃÃÄÂÀ¾¾¿½½½½¾½½¾¿¿¿¿¿¿ÁÂÁÂÀ¿ÀÄÇÉÊËÉÆÄÀ»¶°®®¯±²³µ¹¿ÆÈÈÆÅÄÁ½»º¹·¶³³¶¸ºº»»¾ÀÂÃÄÅÆÅÄÃÃÃÃÃÂÀ¾º¸¶µ°«¨¥££¡ž›™™œ››› £¤©®°®®®«ªªª¨§£›‘…uw}‰’ž¦¬±´²°ª£Ÿœœž¥ª¯´¶»¾¾½½»º¶°«¦¤§°¶¸©©ª²¯£Œ‹Ž‹Ž’š¨µ¦˜’‘ŽŒŠˆ‰ˆ‡‡‚zvtplifcaZRJE=9@gvz…¤¢žŸ›žŸ ž £¦¨©©«¬¬¯²³³´µ·¸¸º»½½½¿ÀÁÂÃÄÃÄÅÆÇÇÇÈÈÇÈÉÊÌÌÎÎÎÌÌËÌÍÍÌËËËÊÊÈÇÇÇÇÇÈÈÆÄÄÄÿ»º¹º¹¹¼¿ÃÇÆÈÇÇÆÄÄÂÁÀÀ¿ÀÀ¿¾¾¿¾¿ÁÁ¿ÂÄÄÄÃÂÃÆÉËÌÊÈÄÁ½»·³°®ª«®¬®³·½ÁÇÆÄÂÂÀ»¹·¶¶µ±¯°²²µ¶·¸»½¾¿ÀÁÃÄÂÂÃÃÃÁ¾¼º¸µ³°¬§¤¤¡ Ÿžš™™œœ›¡¡£¥ª®¬®««ª¨§§§¥¡švtz‚™¢ª¯²²¯¬§¤¡ ¢©®³¶¹»¾¾½¼»º¸´°ª§§®´·¶¢¢¤§©§™‹…†‡‡‡Š“£°ªŸ•‘ŽŒ‹ŠŠ†„|uqmkida]WOD9407cz|…— ¢žœš›žžœ¡££¥§¦¨«¬°²³´µ¶·¸¸¹»¼¼¾¾¾¿ÀÃÄÄÅÅÆÇÇÇÆÇÇÈÉËÌÏÐÏÎÎÌÍÍÎÎÍÍÎÍËÊÉÉÈÈÉÈÉÈÆÆÇÄÁÀ½»¼¼¼¾ÁÅÆÊÊÊÊÉÇÅÄÃÂÁ¿¿À¿¿À¾¿ÂÁÀÀÃÄÄÃÄÄÅÈÊÊÉÇÃÀ¾»·´²²°®¯¯®±µºÀÄÅÄÃÀ¾º¶´³±²°¯±²³µ¶·¸¹»¼¾ÁÂÁÁÁÀ¾»¹¸¶³°¬¨¦¤¤¢ œ›™™›››Ÿ¢¤¦¨ª«¬¬¬¬¬«¨§¥¤¢Ÿ—ow|ˆ’§²±®©¨¦¦¨¬³¶¹º»¼¾½½¼º¹µ²¬§¥¬³µµ´›œžŸ¤¤‚€€„„†˜¨¨ž”’ŽŠ‹‡ƒ~ztplkeb\YQD:41.1Xx}†– ¢ ›šš›žž ¡¢£¤£¦ª¬¯²³´µµ¶¶·¸º¼¾¿¿¾¿ÀÂÄÄÄÄÄÆÇÇÆÇÈÈÊËÌÏÑÐÏÎÍÍÏÐÏÏÎÍÎËËÊÊÉÊÊÊÊÉÉÈÇÅÁÁÀ¿ÀÁÀÂÃÆÈËÊÊÉÆÅÃÂÀÀ¿¿¿¿¿ÀÁÀÀÀÀ¿ÀÃÃÂÁÄÆÆÉÊÉÈÄÀ¾º¹·¶µµµ´²²°°´·»ÀÄÅÄÃÀ»¶±°¬ª«¬©§¥§©¬¬®°²²´µ¶¸¸»¾¿¿¾½¼¹¶µ²¯¬¨¦¤¤¤¢ššš›œœ›œ ¢¥¨ªª¬¬®¬¬«©§¨¥¡Ÿ‘wty‚Œ–¤«¯¯¬¬«ªª¬°µ¶º½½¼½»»½»¸¶³°©¥§²´³²–”“‘•˜ €~{}~€„‡‘ ±¦•Ї|vsojfb\VME?;620/Jv}†”Ÿ¢ ››˜˜›ž ¢£¤£¥¦¨ªª®²²´¶¶·¶¶·¹½ÀÁÁÂÃÄÄÄÅÅÄÅÆÆÆÇÈÊÌÌÍÎÐÑÑÎÍÏÑÑÐÐÎÍÌÌÌËÊÊËËÊÈÉÊÊÊÈÆÅÅÃÃÅÇÇÉÉËËÉÈÆÃÁ¿¾¿À¿¿¿¾¿¿ÀÀÁÀÁÁÃÄÃÃÃÆÈÉÊÊÉÆÂ¾½¹¶µ´´´µ¶¸¶³³´¶»¿ÂÁ¿½¸³°«¨¥¥¥¤£ ¤¦¨¨©««®±´´µµ·º»ºººº·µ´°¬©¦¥£¢¡žœ™˜˜œž ¡¥©«ª«¬®¬¬©¨§§¤ žsu{…ž¨®¯¬¬¬¬®²¸»½¾¾½¼¼»¼¼º¹¹³«¦¦«°±²±°™Š‹Œ™•…~{{zz{~…˜¤°¯©“‰„}vrmgb^ZQIHDB?9736Dk}…šžŸœš——šœžŸ ¢¤¤¥¥¥¤¦ª¬¯¯±±²´´³³µ·»¿ÁÂÃÄÄÄÅÅÄÄÄÆÆÆÈÉËÍÍÎÐÑÑÏÎÐÐÐÑÏÍÌÌÌËÊÊËÍÎÌÊÊÌËÌËÊÊÉÇÈÉÊÉÉËÌÊÉÇÄÁ¿¾½¾¿¿¿¾½¾¾¿¿¾¿ÀÂÃÄÅÇÇÊÊÉÉÇÅ¿»¹·´²°®°²´´³´µ¶¹¼»·µ±§£¤¤¡ ž›œžŸŸ¢¥¤¦¦¦©«®®°±´µ¶¸¹·¶µ´±®©§¦¤¡žœœ›˜——šœžžžŸ¢¥©«««¬¬¬¬ª¨¦¥¤¢Ÿ›‡twŠ—£«®¬«ª¬«ª®³¸¼½½¼½»º»½½¼¼¹¶¬¦¦©°±±±°–މˆ‰‰Š–„|yxyz{|}ˆœ¦«®¦“upe`\WOIGHGCA@=;:Bdz‚Š˜Ÿœ›™——šœžŸ Ÿ¡£¤§§§©¨©¬¯¯²²²³´¶¶º½½¿ÁÂÃÃÃÃÃÄÅÆÈÇÇÉËÎÐÏÐÐÏÏÐÑÑÐÏÐÊÂÊÎÍÎÏÎÏÎÌÍÍÍÌËÌËÈÉËËËÊÉÊÊÉÈÄÁ¿½½¼¾½¿¿½»»¼¼¼¼¾ÁÃÃÄÆÈÉËÊÉÇÿº··´±®¬««ª«®¯³´¶·´ª¥˜–˜™˜•”‘Ž’˜š›ž £££¥¦§ª«¬¯±³µµµµ³²±®ª¦¤£¡Ÿšš™™™˜™žž ¢¤§ªª««¬¬«ª¬«ª¨¥¤¤£Ÿ›‡t}„ž¨¬¬¬ª©©©©®´¸º»ººº»ºº¼¼»»¶¯¦¥¥¦«®¯°¯¯“‹‡„„…ƒˆˆ}yxwvvxy{„‡—Ÿ¬®³®£‘}k_VOMIJIGEBA@=?Yr}ˆ–ŸŸš–˜˜˜˜–šžžœœ £¦¨ª«¨§©«¬®°±°±²´´µµ¸»½¿ÁÁÃÂÄÅÆÆÆÈÇÇÈÊÌÏÑÐÐÏÎÏÏÏÏÐÕ»]_lw‡²ÑÏÏÎÎÍÍÍÌÊÉÈÌÎÍÎÍËÊÊÉÆÁ¿½½¾ÀÀÁÀ¿¿¾¼¼¼»»¿ÁÃÃÄÆÇÈÈÈÆÄÁÀ»¸¸¶´±©¨§¥¥¤¤©¯±±¬¦¢–‘ŽŒ‹Š‹ˆˆ‹•˜š ¢£¦¨ª««¯²´´´¶µ²°«¨¤£¢Ÿœ™šš›š™™›ž ¡¥¨©«¬«ª«ª«©ª¨¥¤£ ™„y€š¨««ªª¨¨§¨ª®²´µ¶···º¹¹¹»º¶®©¥¤¤¥¨¬«¬®®”Œ†‚‚‚…Š…}wusvxxxz~‚‡ˆ“œ£ª°³µ¯¥œ‹xoeZRPIFDBPlz†‘žœ•”••”’•—››šŸ£¦©ª¨©¨¨§ª®¯®¯±²´µ¶¹¼¾¾ÀÂÂÃÅÆÆÆÆÇÈÈÊÍÎÐÒÑÐÏÎÎÏÎÎÎÒ«:8752†ÖÏÍÍÎÎÎÍÊÈÉÈ«¨°´µ·¸ÄÈÄ¿ÁÄÅÅÄÁÁÁ¾½½¼¼¼¼½¾¿ÀÂÅÄÆÉÌËÇý´²¯©©§¡››Ÿ¡£¡¡¤§¨¨ª¤žœ—‘ŠˆŒŠŠ„„Š“–™›¢¥¦ª®¯±µµ¶¶·¶²¯ª¨¦¤¡¡žœ›™™š›œ›œž Ÿ ¥§¨©ª««¨¨¨¨§§©¨¥¤¡ž›–z†”¥¬«ªª©©©¨ª«¬±²±´´³³¶¸ºº»¹²¬¦£¢£§««©«®®•‹‡ƒ~€€‚‚~xurssutuxy{{~‚‡‹’š ¦°³±±°¬¥Ž}jYOKPhuƒšœ›—‘’‘‘””–™™›Ÿ¤¨¨¨¨¨§¦¨©ªªª®°³µ·¹»½½¿ÀÁÁÃÅÆÇÅÆÇÈÉÍÏÑÑÐÐÏÎÎÏÏÏÎÓ™:?>?:–ÖÌÍÍÍÌÍÊÊÒºl><ABDWÂÆÈ‹exŸ¯¹ÁÅÇÃÀÀ¾½¿ÁÀÂÂÁÂÞœ†qbUHD?866732489<>VŸ¢†IFHLMKMLOT]ds‹‹‘—š›Ÿ¢¥§ª¬¯±´¶¸¹··¸´¯¬¨¥£¡ žœšššš›››œœžž¡£¤¦¦¦¨©©¨§§¦¦¥¥¥¤¢Ÿ›’|{¡ª©ª««¬¬«ª««¯¯¯°²·¹¸·³¨¥¡¡¢¦©©©«««™Š†„}z{}~€ztqppqrrsvuxxz~€‚‚ˆ•˜›œž žŸ™‚s_RQaq~Œ”˜›—’“•––˜™ž£¤¤¥¥££¥¥¦¥§«°²µ¸¹»»»¼¿¿ÁÃÄÅÆÆÇÇÇÈÌÏÒÒÑÐÏÎÏÏÐÐÏц;C>>>¦ÐËËËËÊÊÎÊ•E-:<=9QžÎÈÁÆi'..4>IVfs”˜››’‹‰¹ÄÃÉ—/*'&(&'$"!!6›š™¡a!9q•›œž £¥§«®²µ·¹»º¹¸µ°«¨¦£¡ŸŸ›››œœœ›žŸ¡£¤¥¥¥¦¥¦©§¥¥¦¥¤£¤ Ÿœ˜ƒ™©¨©ª¬«ª«ªªª©©«««ª¬°³³²¯«©¦£ŸŸ¢¥¦¨©©««•‹‡ƒ|xxz{}~~ytpopppqrsttvwwx{}}~‚ˆ††‡ˆ‰€vfVO[n‡’–—™–‘ŽŽ””••”–šžžŸŸž ¡¢¢¢£¦¨ª°²¶¸¸¹º¼¾ÁÂÄÅÅÆÇÆÆÈËÏÑÑÐÑÏÏÏÏÏÎÍÐw:DA?@ÎÊÊÊÉÌÒ¯c.3<=?:h¸ÑÉÄÀÄc243310.--/1/1552215¨ÇÂɈ140.,*(''&#""!! 9˜™–›Z " EŽª£§©«®¯±²³·¸¸¹¸·µ°¨¥£¡¡Ÿš™šš›œš›œœŸ¡¤¤¥§¨¨§§¦¥¥£££¤¦¥¥¥¡Ÿš•€’¦¥§©ª«ª¨¨§¨¦¤¦§§¦¦¥¨ª¬ª¨©¨£žœœŸ£¦¨©ªªªš—†€~|ywxyy{}{uqmlmmmnooppopqsrsvvrutqqqrrnnj_UKTl‡””•––’ŽŽ‘’’’”——™™› ¡£¥¥¦¨©¬¯³µ·º¼¿ÁÅÅÄÅÆÈÉÊÍÐÑÑÏÐÎÎÏÎÎÌËËk;B@=C´ÌÈÊËÑË„80:=>9B…ÈÑÇÆÃÀÆ^43225689:9877778:<@®ÄÂÊ~.1/+*)'%$#!"! "! @™˜™N!#$%-†¯ª°±³µ´µ¶¹¹¹¸¶²©¦¢¡¡žœšš›š››™—˜™œ ¢¥§¨©¨§§§§¦¦¤¢£¤¥¤¤¤ ™˜¡¤¦§©©¦¡ ž™––™ ¡¢£¢£¤¦¨§§§§¨¦¡œ™šž¡£§¨¨©©‘“‘ˆ€|zzwuuvxy{xsmjiiiiikjllijjjijljhggfeeecb]WMIMe{ˆ˜•““‘‘ŽŽ‘Ž‹’••–™šœž ¢¢¤¢¢¥¨ª°²´·º¼¿ÁÂÃÅÆÈÉÌÐÐÑÑÏÎÌÍÎÍÌÊËÇ[DC@9I»ÊÅÅγc.4=@<9V£ÐÍÆÇÇÃÁÃU.123242024467:9797=¯ÀÁÉs.1-*%*71/.,)(#" HŸ™”’C(%#"!%$&($.”³°³´µ·¶¸¹º»»¹µ°«§¥£¢¡ž›š››œœœ›˜˜™ ¢¤§¨©©§§¦¤¤¥£¢££¢¢¢¢Ÿ›š—’™¡¤¥¦¥¢œ—‘‰€~€‡˜žŸ¢£¡¡¦©©¨¥ ›˜˜›¢¥¤¤¦¦§_ˆ‡~zyxurstwwxtlifeecccedbcc`b``ac``_^^\ZYXTPF@I_x„˜—”’‘”’Ž‹ŽŒ’’’”•™›ž ¢¡ ¡¤¦ª¬®²µ¶¶¹¾ÀÃÄÄÆÇÊÏÐÑÐÎÍÌÌÌÌÉÈʽL<@@8SÀÃÃÈ”D3::;:;t½ÎÇÆÅÅÄÁÀ½K034.B€yl\TJ:788669Z·½¾Åi10++&v “•“Œ…{tmffdx•“ŽŽ@(x†‚~vY3"$(*-&A«·¶·¸¸¹»»ºº¹¶±©¨¥¤£¡ žœœœžžžœ›šœŸ¢£¦¨ª«ª¨¦¥¤£¢¡¡¤£¢¡ Ÿœš—”“𡣦¤Ÿ™‡ƒyuuu‡’™›œ ¤££¨«¬«¨£Ÿš–™Ÿ¡¡ £¤£¢4^†……‚}{zwtqpqsutmifa`^^][XY[WVTWWWXYXYWUSQOMID;34]w‹“——”’’‘ŽŒŽŒŠŒŒŒ‘’’•–—™Ÿ¡¡Ÿ ž ¡¡£§ª®±³µ¸»¼¿ÃÅÅÄÇÌÎÏÎÍÌËËÊÈÇÆÈ³@48:6U½Â¾s24<881DËÇÀÀÂÁÁÀÀÀ·B256/_ËÇÉÄÀ·¦—”˜©¸¾½¿Äb)-+++‘²©§§¥¢¡Ÿ¡¡žžœ˜“‹47 ¡¤§«®›Q&*,,.'{¾¹»º¸º¼»»¹·²°«¥¦¤¥£¡¡žžž ŸŸŸœœž ¤¥§©ª«ª¨§¦§¦£¡¡£ ¡ œœš–”•› ¥¦£Ÿ•Šytrtv}†˜œ¡¦©ª¬±²±®ª¦£Ÿ¢¥¤£¢¢¢¡ž@5`†ƒ|{vwurooqrtqifc`^\XVURQPMNOOPRRPPOLKID@7,!T}‚ˆ•–”“‘’’‘‰‡‡Š‘“”–˜—šœœžžžž ¤¨«°µ·º¼¾ÂÄÆÆÈÊÍÎÌËÊÈÇÅÃÂÂÄ©93540O¾°V-68760S¤ÆÁ¾½½¾¾¾¿Àó?3451jÈ¿ÀÂÃÃÄÅÌÎÉÆÃÁ¿À¿Â_,.,()’«¤¡Ÿž›š›˜˜˜—•“,= ¡£¥§³«P(..0,O¶»»»»½¼ºº¸µ²¯«¥¤££¡ ¡œžŸ ŸœœœŸ¤¨¦¦§©ª©§¨¦¦¤¡ ¡ Ÿžœ›™˜–“”›¡¥£š“‡{sqnnpuz‡’›¡¥¨ª®°±¯®ª§¤Ÿ¡¢£¥§§¥¥£ŸD@<^ƒ~~{xwutppprrqoida][YVSPPMHFECDIHHGEB@:0!P|„‡Ž“•–•“”––”‘ŽŒŠ‡†ˆŽ‘“–––˜™™™˜™š››œž ¤¨«®´·¹¿ÃÅÅÆÈÌÌËËÆÃÀ½ºº¹¸»™-./.(LŽA&53350d²Ãº¼¼»»¼¼¼¾¾Á¬>4352nÆ¿ÀÀ¿ÀÀÃÄÃÂÀ¿À¿¿½½X++)&*‘¦¡Ÿœ›————˜š™”’†'F¥Ÿ£¥¨©«¸œ6,/12<¤À¼½¼½¾¾¼¹¶³®©¥£¢ ŸŸžžžžŸŸŸŸŸ›œ¢¨¨¨¨©ª¨¨§¦¦¥¢ Ÿ ¡Ÿœš—•–”’”šš‘ƒ{qnnlmmptx{ƒŽ˜ ¡£§ª««ª¥£¢ žœžŸ¢¥¤¤£¢¢DCA8f||{zutsqnnnmnnjf`][XVRQMKHD>;99<=:3+'
Hy‚†Š“”””•””“‘ŽŽ‹‰ˆ‡ˆ‘’‘’”“’”––——˜™š›œž¤§ª®°¶¼¿ÀÂÆÈÌÌÉÆ¿¸²®®®¯°µŽ%)'()+."-.-,1l·º´¶¸¸¹»¼½½½½À§84493qĽ¿ÁÀ¿¾¿ÁÁÂÀ¿¿¾½»ºQ%'%#'ŠŸ›™šœ›–•—™˜˜•‘Ž€#M§¢§©«¬®²·Q*//34“Á¼¾¾¿¿À¾¹¶°ª¦¤£¢ žžŸ Ÿž ¥¨§©ªªªª¨¥¥¤¢¢ŸŸ Ÿœš™˜•“‘”—“…}tllmjmmptsux‰“™™œ £¢¢›‘‘—œ›™š› ¢¢¢¢¡BCA;=jxwyvtrromllljjhb]ZXUSPMJGB;7542(
D|ƒ„ˆ“•–—•’‘ŽŒŠ‹Œ‹ŠŠ‹ŽŽŒŽŽŽ’““““•—˜™˜œž¢¤§ª±¶¸»ÀÃÅÉÊÅ¿µ¥¡¡¡£¦¯€!&%%%$%))++.y¹¸±²³¶¸¸¸»½»¹ºÀŸ53242tȾ»¼ºº»¾½¼¾¾¼»»º¸ºM%&#"%‡˜–•—–•––“’’”Œ‹Žw\ª¤©®®²´»_(1263–ýÀÀ¿¼¹·´±¬§¤£¢¡¡ŸžŸ ¢¢¡ Ÿœœž ¤§¨§©«©©¨¦¤¤£¡žž Ÿœ›™˜—•“‘ƒurkijkloqqtstx}†Ž“˜™š˜ƒ||ˆ•˜––˜šž¡¡¢¡¡CCC@7?kuvxurqnlkkjjlhc^ZWTRPMIE@<851(
=|‚……ˆŽ”•–•”‘Œ‹Š‡‰Ž‹‰‰ŠŠ‰‰ˆ‰Œ’’‘””“’–™œ £¨®±¶¹¾ÁÄý¶ª¡œ˜˜™›Ÿªp!!!!"$%&&%T¶¯¯°²³µ···¸¶¶¶½‘/100.R˜¤®·º¼ÀÀ¿½¼»¼¹¸··¶H"$ #‰¢›œ™–’‘”•’‘‘ŠŠŒm e¯§ª¯°±¶¹¿Y/0588¤ÅÁÀ¾½·´²°«¦¡¡¡¢¡Ÿ ¡¡¢£¢¡ žŸ¢¦§¨§©©§¦¤£££¢Ÿœž ž››š˜–”’މ„tolkiiklnqrsusx~ƒ‰ŒŽ’‘‹€tpqŽ““•–šœžžœ›œCBCA=8>outtrqmmlijhiheaZURRQMID>975(
8x}ƒ„†ŠŠ”•“‘ŽŠŠŠŠ‰ŒŒ‹‰‡†…„„ƒ†‰‹‹ŽŽŽŽ’–›¡¦©±³¶¹´±© ›—•••˜›¤h# ""$$3›®©®¯¯¯¯¯°³´´²»„*--,,'(1>Lau…˜Ÿ¡ §¼¸´±E#!FTSYYZ`dagigkpŠ–c " p±«°³¶¶·¼¬A6346F·ÄÁ¾¼º¶´³®¨¢¡ŸŸŸžžŸ¡¡¢¢¡¢ Ÿ ¤¤¦¦¥¦§§¦¥¢¢£¢¢ š››˜•”“‹€{vqnkihhkmnqrqtv{‚„‡‰Œ…wpor|‹’’•–™š›š–““AA@@>=5Bqrrtqnmkifffffc]WSROMHB=74*
0r|‚ƒ…ˆˆ’”ŽŒ‹‰‰……‰‹Š‹Š†ƒ€€€ƒ„…†ˆ‰‡ŠŠ‰ˆ‰ˆŠ–˜œŸ¢¦¨««©¥™–”‘”––—ž`5 !";ž§¤¦§¦¥§¨©©®¬µt$,+)*+(('&$'/028:;5„Áµ±«? @“’“›_ "&"z´¯´·¸¸»Ãl59573tÄ¿¿½»¹¶µ²¦¡Ÿ ž›œŸ ¡¢¡ ¡¢¤¤¥¥¥¥¥¥¤¤¢¢¡ žš˜˜š˜“‘Ž‹|trqmlighmlnpqruwz}€ƒ‡ˆytomrx~ˆ‘”˜š›™—‘Šˆ@@@?<=;1Jpprqnmkifcacca`[UUPMFB<9+
3m|…„†ˆ‰Š‹ŠŒ‹Š…‚„„ƒ‚€€}}}||~~€€‚ƒ€ƒ‚€ƒ‰Ž’–˜™ £¤¤¡œ—•““–––”šQeTM£››Ÿ¡¡¢¥¦¦§§§e))(*+,+,./25446872‰À³§>Dš˜š¡^##&$µ°µ¶¸½Å~/9875=®Á½½¼»¹·´°©¤ŸžžœœœœœŸŸŸ ¢¢¡¡£¤£¢¢¤¤¤£¢ žžœœœ™˜˜–—”‘ˆzupljifehkkmooqtwz}~€}yslknv|€†‹”˜››š“Šƒ???>=:75,Nnqppmjgedaa_]^[XQNHB@8$ +
)jx€‚„†ˆŒŒ‹ŒŒŒŒŠˆ„~~|xwz||}|{z{{{z|}~{z{z{|~€ƒˆ‘“–™Ÿ Ÿ™—“‘“–˜•–Io˜2j¡–šœ™ ¢¡£¢£U%%'$)'%'+,.0214785޼°¨£8 GŸž¨\#&%(&‡Àº¿À¿ªj/7;:82ŠÇ½¾½»¹¶³®©¤Ÿœ›››››œ›œŸžž ¡¢£££¡ ¡¢¤¤£¡ Ÿœ›žŸš••••“‘ŽŽŽ…ytpmhfeeeilmnpswz~}}|zvvnikrx~~‚Š•›ž›–Ž…w?>>=<:85/)Kpllkihfcb_][ZXUOIE@.
+
%ct{ƒ…†‰‹‹Š‹‹‡…‚€|{zxuwxzywyxvwxwwxxuttuwx||{}€ƒ‡Œ“—›œ—–’Ž‹Œ”–‘Cq™~ x™’”–š››œžŸ¦F!"#)~ŠveTJB:965767’·©£›0K£ ¤¨V$&&)#^ˆƒzs[:+49=<1xƽ¼½»·³°¬¦¢Ÿœšš™š›š›œžŸžŸ¡ŸŸ¡¢ žž¡¢¡ ŸœŸžœš—‘‘“‘Ž‹ŒŠ‚vqmlifdcehkmnsy}yxvvtpghmrx||€Š•šœ•Š‚{w?==;;8641-*Gjkgffdcc_]YVRLLE,
+ +
%_ryƒ„ˆˆŠ‹‰Šˆ‰ˆ„ƒ‚}{zyxvwxwvuvvttttttqorsstwwwww{~…‰•—›š—”ŒŒ’–‘:m‹‘g&„Œ’”•–˜˜™›œ7!! 2¦¸¹»º¸²«¦¢œ™––¨¬¡š+*UTTQONKIHIIKJrª¤¤§N$''))&))%(*/34:6:}Á¿¾¼¼¸³¯«¦¢ž›šš™™š™››œžžž žžŸŸ ¢¢ Ÿžžœ›œ™–”’’‘ŒŒŠ‹‰spmkidcdfiilqz‚…ƒ}wuusrqkchoqv|€†”›ž—’†xw><;;:54411/):_gfb`ab_ZUMKI:!
+
Wrw}‚……‚†‡„‡ˆˆˆ†„€|yzzvwvuutvtssrsrqompqptutwwvwy€‡ŒŽ–›™•“Ž‘Œ6n‡…‰J6Œ’“”—˜—‘.7Ÿ¦«²µºº¼¾½¿¿¾º±¥š“‚%C”‘‘”••—œ£¨§¥¤ªL$((*,-020234213PŸÊÀ¿¾½º³°¬¨¤ œš˜™˜™™™™šš›š›ššššœ›œž ¢¡¡ Ÿž››››š™–•“‘ŽŠ‹‹Š‰‡~vtpnhcbegklqw„ƒxvtqonlhdhnow‡’ ž™ˆƒ{ww><::85434311,1Oa^\\ZVPNL7
+ +
Lqx|‚ƒ‚‚‚„…ˆŠˆ‡‡‚{{ywwvtuutttrqsrrpooqprrqssttx|†‡–—–“‘‡‡†ˆ‰‰6o~|}‚4G‹ˆŠŽ‘’’”‡%:˜š¢§¬°²³³³´²°¬¥šŒw J‹ˆ‰Œ‘’–—™œ¡§¨ª©¬M&++-/-+.,-1/?b‘ÁÌÂÁ¿»¸±®¬¨¤¡žš˜—˜—–“’“””’‘••”’’–—–™¢¡Ÿš˜—˜˜–“““’‘‹ˆˆˆ‰ˆˆ„}|ysnidbdhmu{ƒˆ„€yuuspolhdefimw…“›¡¡Ÿ™”ƒwuw<;9877655331/.)?ORRQLIA"
+
<t{}‚ƒƒƒƒƒ†‰‰ˆˆŠ„‚~~{yvussssssqqrpqqpnqpqpopqrtwz}„‰‘••’‹†„„††‡5"w€{{|t#b‹„†Š‹Ž|<”’”•𠤣££¡Ÿš“Žˆ†jT‰†Š”–˜œ ¥¨ª¬¯±M)/164>LVas°ÂÉÅÁÁ¿¹·´¯¬©¥ œš•“‘‘ŒŠˆˆŠ‰ŒŽŽ‘Ž‘•–˜˜••–“’‘”“Ž‘’Œˆ‡‡††‡ƒmtxslfcaensy†ˆ†{tpqonkecfghio‚“¡Ÿ›•ˆ‚|xwz;::877442320.--3?A@EG5
+
*p{~ƒ„„„……†††††ˆˆ…~€€}{wvtqrrqrnqqqpnmnoppponpqruxz~†Ž’“’‹ˆ„‚„„/"uƒ‚~{€fp‡ƒ……ƒ‡t>‹ˆ‰‰‹ŽŽ‘’Šˆ„€€…a]‡†‹ŒŽ•–™Ÿ¥«¯±³¶Q-16;<ŸÁÄÉÌÍÏËÅÃÿ¼¶²¯«¨¢™–’Ž‹Šˆ‡ˆ†…„ƒ„„ƒ‚‚…†„ƒƒ„†ŠŒ‹Š‹Œ‹‹‹‹‰†„„ƒƒ„‚aanmjeabhmu|‚„…‚{tqoomlhb`cgiiju„”š—”Žˆ„‚zwz€97788842310/,0;A@;8=4
+
$c{€‚„„„…‡††‰‰†…‡…€€~}{xwurqsspnoooonnmnoooppqruuwz}‡Ž’’’Œ„})!wƒƒ€{Y(ƒ~€€~‚jDƒ~€ƒ‚ƒ‚‚€~}~‚Ueƒ†ŒŒ“–—ž¢¨±³µ´µQ2369H½ÍÉÊÉÇÇÇÆÄÿº·²®ª¦¡œ—‘Š…„€€€}}|}}}|zzzz{z|}|ƒ‚ƒ…†ˆŠ‰ŒŠ‡…†Š‰‡ƒ‚‚€~€‚€bYdhecabgkxƒ„|upnnmmjd__cfhhjmr|‡‹‹ˆ‚€}ww‚5676665332/,1BGDA:90
+
_}‚„……„‡ˆˆ‹ŒŒ‹‡†„‚€~}|{zwutttpopnoopqpprrsuvxwxy{{}‚‰’’‘‡v%&w€~~{y‚G=…~||z~_H…|}€}{||yxxz||}„Ks‹‹‘”˜›ž¥§«°´¸¹¸´J1344J¹ÆÇÈÉÈÈÈÆÃ¿»¶¯ª§¢œ˜”ˆ…ƒ~|zzzxwvxywvwvuuvtvyz{y{~~„…ˆ‰†ƒ‚ƒ‚~}}~~}`[`fccbcglw€ƒ‚|wsoonlke_^`ceggjkmsz‚…ƒzxz}‚‡578645542219EIGB>?:
+ +
Ly‚ƒƒ„„†…†‡‹‹‡……ƒ€~|{xvuusurqqqpqrqrtuy{~ƒƒƒ‚‚ƒ€„‹ŽŽŽŽ‰‚}{m )z}{{|}}~~4Mƒzzx}S.RV[bdhopsvv{~|€~„E"ƒ›—–›ž¢¤¨«±´¹¸¹¹»´I5693M¿ÆÈÊÉÈÇÆÃ¿º´¯§ š–Šˆ…€}|zxwvwvttutvtrrsrsqppqsuvxyyz|~€‚‚‚~€~~}zzyz}{`[`baabehox€~|xsnmlkhb^]beggeffimx€€}yw|ƒŠ7865533414BJIGE@<@
+
:x‚~}€€€ƒ„„……ƒƒ€}{zwxwuvuurrrqrru{€…‰Ž”“’“Ž‘‹‰‹‹„}yui"({zzy{|||v&^ƒxv{M#(+06:R~„=`twz†‰–˜š›žŸ±»¼À·H7776XÆÊËÉÈÆÃÀ½¸²¦ —’Œ…‚|{yvuttrrrqsttrqqpqooooooqrsuvuvxyx||||||~~|{zvvvvz|x`[___`bcis|€ztnmjigc^\`dfeggbglp{}}{vyˆ•77534322>JPJIFD>2
+ +
.wƒ~zyz|}}€ƒ…„ƒ„†„ƒ}}}||yxwwvrrtvz†‘—𠢤¤¥¥¢žš—”Œˆ†ˆŠ…|wro_PJE>Gwxzy{yzxx|dl{rwF/€€5!$&)+.1147›ÂÀõF7887hÌÈÈÇÅý¹´±¬¥ž—”Žˆ€|ywwwtrsrsrqprrqponllmmopnopprsrrstuuwyxxxyyzxwvtuvuwzv]Z[]_`delu}‚‚|tojkhie]Z\bdgffhfms{~|{{vv|‡™Ÿ7553229JVTPJHE:/!
+
*f€|xxwz||~}~‚‚„†„…††…ƒ‚ƒ€{zyzyvy|~†Ž– §ª¬¯³±²µ´±¬©¢™’‰„ƒ…†…€ytoqsx{{wvwx{{|}|{z€V#&pvyB5€0"""%'(*+,//15ŸÆÁÇ®B:;<7rÍÈÆÃ¿½·²¬¦Ÿ™”Œ†€}zwvutssrrqrrqqpomlkjiklmmnmmooppppprrsttvusstuvuuututuwoZYZ^aceinv}€|vqniigfa[[aeejhhimu|€|}|zvz†”œ¢¨52123ASWTSKE<70,!
+ + +
"[y~zwtrswwyz|€€„††‡†ˆˆ……ƒ€{{{yz‚‰‘™¤±³¸¹»»»º¹¶´±¨œ„€‚„ƒ‚|wtrqprtsstuyz|}~}}|sjbXOI;[ww@A‚~ƒƒ. ""#&')*-/2359©ÇÂÇŸ76530…ÌÅľ»¶±¬¥ž™Ž†‚|yvutttrrrqqqppoomkjihhhghijijlmmmnopoqrqppqssrsrttstrsssukVW]`efimsy€yrnlgghe\Y\aefjkjnt|„…~{zvx‚‘œ ¤§31/-3AA?A=75320/#
+ + + +U{{wrrqrrstyz}€ƒ„…ˆ‰ˆŠŠ‰‡‰‡…‚~~‚…‹“š¥³º»¼¼½¾½º¸µ±ª£˜Š~{|~€€}zyyvuttuuuvz|||~}|}}}|x{}|{xxoaZSKD<62,)$#I‚€†) "!#%')*+-33/<ÃÂÆ›S\bv‹µÇþº³®§¤ž•†~zvuttsrqppqppoommljhhghhfdghfggijllmmnnmnpqppqqrrpqrpqqrpqtqbadfhjntyƒ}wojigge^XY]cfjjlpsyƒ‰†}yut{‡“œ £¤1.,./.,.43334422'
+ + + + +
Lyxwtqpqrssty|„‡‡‡ŠŠ‰††††‡‰†„‚ˆ‹’›£«²·¹»»»¼½¼¹¶²«¢—Œ~{zz}ƒ‚‚~{zzzzy{zz|{{€‚|zzz{}|z€{zyzywxxwspnlhcYTQl…‰ƒ?2.2300.1656;@FGNSZv¾ÂÅÆÃÂÊÏÒÐÉþ¹´®ª —ކ‚|xvsrrqopooopoollklljjijmlifiigeffiklllmmnlnpomnopomoqponnnprqighjiovƒƒxqlihge`YX]cfijjmsw~…†€zvqs‹”›¡££.,+-.-/043566753
+ + + + + + + +
Euttsrsssstux{‚†‡‡ˆˆ†‡ˆˆ…†ˆ‡………‹Ž— ©±¶¹»¼¼¾¾¼¹´°¨š‚zxyz}…………‚€~€~ƒ‚„„„‡…‡†‚‚‚€ƒƒ„„€|{xyz||~}~€‚‚ƒˆŽŽ‹ŠŒŽ‘““•™ž¡¤§¦©¬±¹¼¾ÁÆÍÊÈÉÉËËÈÉÇž·²ª£›‘‰„~yvtsrponmmmnonoomklnnnoorvurpmjihhfegjjilloonoononnmmlopnmlmmqpkijkoxˆ†{tnieeb`XV[_dghkmqw€‡ˆ€zwurx‚Œ”™ £¦,+,./00224677742
+ + + + + + +
:wwvvuvttuvwxy~ƒ„ˆŠ†…‡‡ˆ‡‡‰Šˆ‰Š‹•œ¢¬²µ·º¹º»º¹³¤˜‘yutux{}ƒƒ†‡……‡…ƒ„„„„„ˆ‹ŠŠ‹Š‹Œ‰‹‹‰‰‰‹‹ˆƒ~ƒ‡†‡‰ŠŒŒŠ‘“‘“—˜œŸ£¤§¬²¹º¹½ÁÁÃÆÅÇÊËÉÊÊÊÉÈÉÇž·°«¥šˆ‚~{xtqrrponnlmnnnmoomnopprsux{yxvrqonkigfgigiklnnonnolllkkklllkmnpoljkqz‚„‡…zslhfccaZTW^dfhikqv{„‰…zvssu}‰‘˜œ ¤¥+++-/00344455762
+ + + + + + +1s{}}}|zxvvwwvzƒ†‰‹ŠŠˆˆˆ‡ˆˆ‰‹‹‘•™ ¨¬±³¶¸¸¸¸µ°¨ž˜ˆ~usqqqty|€‚†ˆ‰‹ŒŒ‹ŒŒŒŽ’““”“‘‘’“•“’“’Љˆ‡‹ŽŽ’”•““•˜——™˜˜¡¤¦¨©°°·»¼¾ÁÅÅÇÇÈÊËËÊÊÉÈÇÈÇžº´ª¢›’‰~zwutrqpqpnnnnonooopppqqsstwxz|{{zywvutppjhhhjklmmmlmnljkkjjjjkijnrplov{„‚|rjgedb_]USX^cegimsx€‡Šƒxurrx‚Œ“šž ¡¢)**,-./11214112*
+ + + + + + + ++n€„‡‡…„€|zzzzy~…‰‹ŠŠŒŒ‰Š‹‹ŠŒŽ’—𣫮°²µ··´«¡œ–‹ƒ}vurqqsuw|€ƒ‡‘””••••–˜š›œœž¡Ÿ››žš™™˜•’Ž‘”““•—–™šœŸŸœœŸ§«¯±´¹¼½¿¿¿ÁÃÆÇÇÈÊËÊÊÊÊÈÈÈȼ¸´¬¡™‹‚~yywuspnonnnmnnnoprrrrsttuuxyz{}}}}}}{|{zxunkjkkmnkllmmlljlkjihhhikmqnov…‡…~yrleddb`\VTV\bdfilot|„Š…}wsot|‡•—œ¡&''(())(''''%$#
+ + + + + + + +"k‹‘Œ†ƒ|~}}„‡Š‰‡ŒŽŒŽ‘•™¤ª¬®¯±´²£šˆ€|yustsqsx}…‹’—™¡£¤¤¢¡žžŸ¡¤¥¦¨©ª©¨§§§¦¥£¡¡Ÿš–•—ššœ››œœŸ ¡¢¥¨¦¥¥§°³µ¸¹¼¿ÀÃÄÄÅÆÇÈÉÊÊÊËÊÉÈÈÇÇÇý¸°¦•Œƒ}zxvvspnmmmmlmnpqqstrrstvwwyz{||}}€€}||}}|{xqmlkmmklkllmlkjjjiighfghiojju€ƒ{qjd^``^_ZRVZadefkkox~„…}xvqqy€‹‘“–™œŸ!! # ! !!"!
+ + + + + + + +
]˜›š˜•‘Œ‡„„‚~}‚…ˆ‰ˆ‹Ž‘’‘’”–™ ¦ªª¬¬°±¬¢™…|{xusutsyˆ‘œ¦¬±¯°®°´µ·³¯«©ª¬¬®±²¯¯®¬¬ª¨¦¡œŸ¤¦¦¤¡ŸŸ¢¦¦§§©¬®°²³±´·¸»¾¾ÁÄÅÆÇÉÊËÌÌÍÌÌÌËÉÉÈÉÇÆÀ¹´®¦š’ˆ‚{yxvtrpnllkjlnppqrsuvuwvwxz{{{|}~}~~}~}}}||ytqnnljkljljkjjjjhigfggfgjjYU_ijhfg`a^\Z]]ZTRX\`ccdgmpw}€|xtrorz…Ž“•˜™žŸ"""" !!##"#"!
+ + + + + + + +Nˆ—Ÿ¡Ÿžš—‘Љ†ƒ†ˆŠ‹ŒŽ‘’’“”–™œ¢§ªª©ª«§›’‰‚€~xwvyz~‡˜¥«£‡mWMJJP_œµÂ½³³³³³´µ³´µ´´±¯«¥¤¦«®¯«¨¦¦§«®°°²²µ¹¼¾¾¼½½¾ÁÂÅÅÇÈÊËÌÍÍÎÎÌÌÌÊÊÉÈÇþµ¯¦ š‘‹|xvutrqonlloqqssrqstvwz{{{|}~~}~~€~~}~}|||xurppnlllkkjjjihihffdeeeicMJKSSTWYXY[[[[ZUQTX]bdeehlqx~}xurnpv€‰–˜˜š›œž !!"""!"
+ + + + +<€‘Ÿ¦¥¥£ž™”І„ƒ…Š‹ŽŽŒ’““–™š¢¥§¦¦§¨ª©¥™‹‡†‡†ƒ…ˆŠ‹‘š¨±–c<0034453048Gw¯Ä»¹ºººº¸¸¸¹º·µ³±°²¸º»¸³±¯¯²´³´·¸¸¹¼¼¼½¾ÀÁÀÁÂÄÆÆÇÉËÌÌÍÍÌÍÍÌËÊÇÇÅÁ¼³§ ™Š{xursqpponokWZ]aitvuxxxz|~~}~~~€€‚€~~}|||zyxusronnlklljifihfbcbbcf[FIJJMPQRVY[ZZZVQRV[^_cfeimqy}ytrpou~…Š“™™—š›ž !!"
+
+ + + + + + + + +
)q”Ÿ¥©©©¥¡›–’‹Š†ƒˆ‹‘Ž’••—››ž¡¡¢¡ £¦§¨¥¡œšœ›š›Ÿ£¤§«·§f0(/7;889:9<::5.@{¼Ã»¼¼¼¼»¼½¼º·¶¶¸¼ÃÆÊÆÅþ¸¹ºº»½¼¼¾¿ÀÀÀÂÄÄÄÄÆÅÆÉÌËÌÍÌÌËÉÊÉÉÈÆÄÀ¼¹°©¢”‡ƒzwutqrqpppoy>j{xxz|{|~~~~‚ƒ„…„…„ƒ‚~}}}||||zywvtrollkkjhghgeaababgM=BFIKNOQVYXYXWQOQVZ^`bdgkotyyuopqot…Š’–•˜šŸ
+ + + + + + + + + + + +`Š—¢©«ªª©¤žš–‘Ž‹ˆŠ’“’“‘“–—™šœŸžŸŸžŸ¢§¨ª®²·¶²²²´·¹¼ÄšB'1524:95457;96784.Q©ÉÁÂÀÁÂÁ¾»ºº»¾ÄÇÈÑ—q‚Ž™¶½»»¼¾¾¿ÁÂÃÃÃÄÆÆÆËÐÐÏÎÉÊÉËËÊÈÉÊÇÅÄÁ¾¹´£§c&*),+Mxstrqppont[f|ywxz{|}~€€„†‰‹‹‹‹‰ˆ…„‚}{{||}~~{zzxusrqnllkjhgfccaaac=3<BGIKMOQTWWWSOOSY[_bbdgkptxvpqqosx…ˆŒ’’”•™
+ + + + + + + + +
Gƒ’Ÿ§««¬¬ª¦¢˜“‘ŽŽ”–•–——˜™š›œœš›œ››››Ÿ¦°¶¼ÂÄÂÀÀÂÃÅÆË‹5172692.5=?;963367583A¡ÍÂÃÄÃÂÀ½¼¾ÃÆËÌÒ¯:-343‚ƺº»¾ÁÂÁÁÂÅÆÆÆÅ˨ynad¼ÊÊÉÉÉÇÆÄÁ»º¶°© ››D6trsrqqpnpp'f~yyyz|}~ƒ†‹Œ’ŽŒ‰‡‡„~|{}~~~||{xuuusqnnlkkkheb`cQ248<BFIKMORTUSNMPTX]^_`deioswqppons|„„ˆ‘”˜ž
+ + + + + + + + + + +1t›¤©«¬¬¬«¦¢Ÿœš˜”“–—˜››™š››œ›š˜––—˜™ ¬¸¿ÄÆÅÆÈÊËÌÎ|)1:7723Mw³¦c=0:98=;>œÉÂÃÃÃÃÂÄÇËÌÌÌÆV16697P»¿¾¼¾ÀÂÃÂÃÄÇÇÇÇÏo0240KÈËÉÉÈÆÄÃÁ¾¹¶°¨ š“‰//qrqqqqpoyHi€|||}~€‚‡Œ”••”“‹ˆ†„~€€€}~}zxxwvurrrpopmlhdd>.379>DFJLNQSSPLOSUZ^^]_dghnsspnoprv|‚‡ˆ‹ŒŽ‘”˜
+ + + + + + + + + + + + + `‘Ÿ¦¬°¯¯®«¨¥¤¡›˜™˜˜›žžœ››™—•’‘‘‘“˜¢°ºÁÅÆÈËÍÍÏz-58::0N•ÁËÊÈÈÉÊÌǯf59;;:.PÀÆÃÂÁÂÅÉÊÌËÄÇ{+633576œÅ¾¿¿ÂÃÄÃÆÅÇÇÉÊË\49:4eÍÇÇÇÅ¿¼¹¹¶§ ™‹t!-oqqrrsqsck€€‚„†‹‘”˜™˜—•“‘Œ‰……„‚ƒ‚€€€€~|{{zyxuxxvwuturp]/+18;;ADGJMPRQMKQTZ]]^]`dginropootv{|€‚ƒ…„†ŠŒ’–
+ + + + + + + + + + + + + + +
R€Žœ¤¬²µµ´±««©£Ÿœœœœœž žžœ›•’ŽŽ•¨´»ÀÃÇÊÉÑŠ-4696/sÁÑÉÅÅÅÅÅÆÄÁÃÃ…79<1MœÃÄÃÁÂÅÊÌÎËÂÆ¢//10114.kÅ»½ÀÂÄÄÅÇÇÇÈÉÍÁH6885~ÌÄÅÄÀ½º·²§Ÿ™”‡ƒ`,npqqrsrw1n‚‚ƒ„†ˆ‰Œ“–™™™˜—•’ŽŒ‰‡ˆˆ†††††‚‚‚‚€|~|}|{{{|}|{zyws3%+-3:;?BEHMQRPJJPUY]^_`acdinmlmoswz„…„‡†‰Š‹Œ“
+ + + + + + + + +
=yˆ˜¡ª±´¶·¶³®¬«¦¢¡¢ ¡¢¡ Ÿž™“ŽŒŒ‰‹Œ”žª³¸¼¾Àͱ707<71ƒÎÎÇÈÈÈÆÆÅÆÆÅ¾ȉ32a¸ËÃÄÅÆÉËÌÌÉÀ¼¹H&0/0001/?³½¾ÁÂÄÆÆÇÇÇÇÇË«:9874‘ËÁÁ¾º¸³¯ª£œ˜‘Š‚B#kpprspyRtˆ†ˆˆ‰ŒŽ‘•˜™œ›™—•‘ŽŒ‰Š‹ŠŠ‹Š‰‡…„‚~~~~€€}||U#*,7;?ADFKPOKIJOUY\]_``ceiljklov~ƒˆŒŽ‹ŒŽŒŽ‘’
+ + + + + + + + + + + +
'j‚Ž™¡¨¯²´³°¯®®ª¥£¥¥¥¥¤¢¡ žœ—“‹ˆ‡…†‡ŠŒ’©¯²´¶Äs+:<=2ƒÒÇÅÆÅÅÈÈÇÇÇÆÅÅÃÃÉ„ÆÌÅÆÇÇËÍÌÊÆ¾´¼n!--../1241†ÆÀÁÂÄÅÆÅÆÇÇÇÏ”4:889ÆÀ¾½¹´®©¢”Šƒ|zy.eqrrrshv‰‹‘‘’”˜›ŸŸžœš–’ŽŽ‘ŽŒŒ‹ˆ…„‚€€‚„‚€~|r# +!(,7<?BGJLKJGJMRVYZ\]\acehijot}‡”•”“”•••–••“’
+ + + + + + + + + + + + + + + +
Rx…“ž£¦ª¬¯²²±¯«§¦§§¨§§¦£¡ ž˜‰„„ƒƒ„…†Œ’ž¤«°¶´D-6:5XÅËÇÈÈÈÈÇÆÇÇÇÅÆÆÅÃÁÇÊÇÆÆÈÉÊÌÍËÇÁ·¸”,+,./-/2286VÃÂÂÂÂÂÂÃÅÆÆÅÍz5:89?«Á»¹¸±¬¨Ÿ™ˆ…€|xycZtrrsz;#|Œ‘’’““”—™šž ¡¡žœ™—”“”••••“‹ˆ‡…‚‚‚‚‚‚……ƒƒ‚{~B
#(.8>AGJJJIHJMPUXYZ[\_abeknt~‡Œ’”“”—™˜———˜™˜˜
+ + + + + + + + + + + + + + + + +
0u›¢¦ª°°²±¯©§¥¦§§¦¥¥¥£¢¡›“І„ƒƒ„ƒ„†Š“Ÿ¨°¹¡-+052ÌÄÆÈÈÈÉÈÇÆÆÅÄÅÅÅÅÅÇÆÆÇÇÊËËÌËÅÁº¸³E(0./+::279:9¦ÅÀÁÁÁ¿ÁÄÆÅÂÆa3979E¯¼¸²§£œ’‹„ƒ~zxu|ADtsrxZ)…‘‘•––––˜™š››¢£¢ žœš™™š™™š™—•’‘ŽŠˆ‡„…ƒƒ„„†ˆ‡Šˆ‰ƒ|j"(+/9?CHIHFGHLNRUXY[\]^_ajr~…‹Ž‘”•———•”—›œžœ
+ + + + + + + + + + + + + + + +e€Œ™¢¨«¬¯°³²°®¬ª¨§§¦¥¤¤¥¥¤£¡˜ŽŠˆ……‚‚‚ƒ…Š“ž¨´‘%(),2¦ÄÁÄÆÆÇÇÈÇÆÅÄÃÃÄÅÆÆÈÇÇÉÉËËËÉÆÂ½¸¾s$/0/01˜x-;8;6oÇÀÂÁÂÂÂÅÆÄÃÃS4765F°¶±ª¤¡™•‚€|yxutn"$%prwo"*.“—šœœ››œ›œ ¢£¢¡ŸŸŸž›™—”‘‹‰‡ˆˆ‰Š‹Ž‘’‘‘Š:
&).4<AFFEDFILNPRTYZY]]\]amy‚„ˆ‹“””–”•—›š™—
+ + + + + + + + + + + +O„ˆ– ¨¬®°±²´²°¯¯®ª«©¨§¨©¨¨§¥¤ž•‹†ƒ€ƒ„Š“˜¥ "$)3¨¿ÀÃÅÇÈÈÈÈÇÇÇÅÄÄÅÆÆÉÈÈÊÊÊÉÈÆÃÁ½Ãš11114,wʪ96589C¶ÄÂÃÃÃÄÆÆÃĺF6763D®¯¨¤ž™’‡€}{xutxN#NWw|F8Y5Œ—›žŸžžžžœžžŸ¡¢£¢¢¢¢¢¡ Ÿ›™—”’‘‘ŽŽŽ’•—š›žš™“`#)-4:@EFDCEIKNPRUVXZ\\[[[`fnuz€…†‰ŽŽ“•–”•“‘ + + + +
+ + + +
+ + + + + + + + + + + + + + + +,w„“¥«¯°±²³³³²°®¬¬¬¬¬«ªª©¥Ÿ–Œˆ…‚€€‚„Š›~ "* ½»¾ÀÄÅÆÅÅÅÅÅÅÅÅÇÇÉÊÊÊÊÊÉÇÆÅÅÂñ@.4271L¼»¿`066:2‹ËÁÁÁÄÅÅÄÃİ?7640D¨£žš”Š„€}{xutvt-:h-|j}UB—•˜œž ¡¢¢ žžžŸ ¡¢££¤¥¥£¢ žœ››š—””“”“”–˜›ž¡¤¦¦¥¥¡‹, +!'+29@DCCCEIJJMPTUUYZYYYY[\^hmrx{}ƒŠŽŒ‹‹Œˆ… + + + + + + + + +
+ + + + + + + + + + + + + + + + +
\‡— ¨¬®®±³¶·µ³±¯¯®®°±¯®¬«¬©¥ ˜ˆƒ€€ƒ‡$‡º´¸¼¾ÀÁÁÂÃÃÁÁÃÄÇÈÊÊÊÈÇÇÆÅÄÂÂÂÃ]*63352›Â·¿.6685VÃÀÀÁÃÃÃÃÂÆ£7710)D£›—“ŽŠƒ|yywust`Iw3U7\“OI—–™œŸŸ¡¢£¢ ¡¡¢¡¡££¡¡£¡¡ Ÿžœœš˜—˜™š›¡¢¦©«°¯®¬¨¨i
+
$)28?CBBEEFGGJNPSSTWWWXY[[^cfiortxx{€€€€‚ + + + + + + + +
+ + + + + + + + + + + + + + + + + +
9u…‘¥«®¯±²²³´³±¯®®°±°¯®®®ª§£ —އ„~}~„Š6U±®²µ·º¼¿ÀÂÃÂÁÃÄÅÈÉÇÆÅÄÃÃÂÿÈ~'2212,sǺ»¿·B47587 ÇÀÃÄÂÂÀ¾Æ”/2-+#AŸ”މ„~zxvwvtrwEY}Y2–LR˜–šŸ¡¢£¥¥¤¤££¡¢¤£ ¡¡Ÿ ž žœœš› ¡¤§ª°²´¶¸¶¯¬9 + + + + +$(28>BBCDEFFGGIMRRSVUVWX\`_cfhlnoonnsyxy{€… + + + + + +
+ + + + + + + + + + + + + + + + + + +fŠ˜ §«®°±²°²´³²¯°°°°¯°¯¯¬©§¢œ–‘Œ†~~~~‰\¨°¶º¾ÁÂÂÃÄÅÆÆÆÅÃÄÅÄÄÃÅÃÂÈ£00113+I½À½½½Ãm08552lÇÀÃÄÃÂÀ¾Å‡+,(%!A—Іƒ~|wwxvuutsv1f|}/yš–M\ž›žŸ ¢¤¦¦¨§¥¤£¢¢£¤£¢££¡¡ žœœœž¢¥¨®±³·¸¹ºº¹¶²¬_
+ + (07>AAEGDGHFGJMPPQSTUVXY[_acedgihkmquw~„’˜ + + + + +
+
+ + + + + + + + + + + + + + + + + +Nz„“ž¥¨¬¬¬¯±²²³µ´±²²±±°²²²¯®«¨¤ž˜“‡ƒ€€~~~!F¥¡§²¶¹½ÁÃÃÄÅÄÄÃÅÂÂÄÃÃÄÅÄÈ¿N,300/7¤Å»»¼¹¾œ13223C¶ÄÃÅÃÁ¿¾Ãx%&" Eƒ|yxvuuvutttr)p‡hO£š™Hd¦ž¡¢£¤¥¦¦¦§¦¤¤¢¡¢¢¡¡¢¢ ŸžžžŸ¡¦ª¬°´¸º½½½¼»ºµ´‰# + +&/8?BBFJFEJHGILOOOPQTVXXYZ\^^acdhjnr}ˆ“œ¢¥ +
+ + + + + + + + + + + + +
It€Ž›¡¦©ª©¬¯±³µ¶´²±±°°±±°±²²¯§¡œ•Іƒ~}~†Z]¥¡§¬¬¯´»¾¿À¿¾ÂŪÂÅÂÂÂÄÄÃÊl-630.,€Î¾ºº¹¸¹½M-3251ŽÊÂÅÃÀ¾¹½l!% NŽ~|xutuuuuttui"%z‡‰A&‘£ £F ! k§¢£¤¤¥¦¨¨§©¨¦¥¤¢£¢¡¡ ŸŸŸŸž ¥ª®²µº½¿ÁÂÁÀ¿¼¸µ§J + + + + $-8?EGIKJFGIIIHLMLLPRSVXWWY]^`bfltŠ™¢£
+ +
+ + + + + + + + + + + +
Tk|‰”£§ªª¬¯±´·¹¸¶µ³²²²°¯°°°¯®¬§¢Ÿ—‹†ƒ~}}€€„6b¤¨«¯´¸¸»»ÂÂ~?vËÁÁÂÃÀÌ19632/.[…Ÿ²¼¿ÁÂÌ€,5460^È¿»¶ºa !V…}{xvuuuuvvuwc*„’œ}"d®££¨M!!"lª£¤¤¥¦¨ªªª«¬¨¦¦¤¤¢¡¢¡Ÿ Ÿ ¡£¨²µ¹¾ÀÂÂÃÂÂÁ¿»³®k
+ + + + + +,7HGFHJKIGIIIGIIIJLOOTUUVY^`cnt„—›œš™•“’“
+ +
+ + + + + + + + + + + + + +
agm€Œ—¢¨ª«®±´µµ¹¹¶¶´³³³²³³±±±°®¬§£ž•ŽŠ…‚}~€y$G•¦§®®®³ºÁ®_/83ɾÁÁű?56651--&$,>Qit{…m364459¬ÄÀ¾º´®²S_}{yyzyvwvvxzy|[/›žŸ§V 1¢®ª©«U!$o®¤¥¥¦¦¨ª«ª¬«ª¨¦¦¤¡¡¢¢ ŸŸ ¡¤ª±µ¹½ÀÂÃÅÄÃÃÂÁ¾º°‹*
+ + + +/HKFHJJKKKIKIGHJKNPSSVZ_cglsŠ‘‰‰Š‰‹‹ + +
+ + + + + + + + + + + +
$hfg{‹”Ÿ§«¬®±µ¶···¶³²³²²²²³´´´³±°¬¨¤–‹†ƒƒk.g‘§®°²l9'5997’ÈÁÄÄb287670+*+)*-*+-.0076446,tº¸¶°§§Bk{wwyyyyzzz{}†W0˜¤¥¥«ž2#$s¶®«¯] #$k®¨©¨§§¨ª¬¬ª©©¨¦¥¢Ÿ ¡¡ ¡¢¦«±¶»¿ÀÂÄÅÆÄÃÂÁÁ»²£F + + + + + + 3HFIJJKNQQNPRPRTWX^`adjnruz|~|~‚ƒ…††
+ + + + + + + + + + + + + +
'kda{•ž¦ª®°²³µ·¸¸·¶µ³³´´³³µ´³³³¯¬ª¦¢ž˜‘‹ˆ…€€}{ƒc'E\a[C+ )12482O»½Ä‡3989:3.%#&'+.232367754351D·ºµ²§ š7$xyxxzzz|~~€‚†Š“` 1¨««¸v'$=¬²±°¬¯`"%$b±ªªªª©©««¬«ª©§¥£¡Ÿ ¢¢¤¦©±µº¿ÂÂÃÄÅÅÃÃÁÀ½¶¯m + + + +6@DCCGMQWWWYYXX\_adceikmoqrtttvx|}€…Š
+ +
+ + + + + + + + + + + + + + +
,nf_o‡’›¦®±³´³³¶¸¹¸·¶µµµµµ´µ³³³²¯«©¦¤Ÿš“Ž‹†‚€{z€`!&(,03.<¿À=79:=6Kš‹`='$(./1466242256*ˆ½±ª¡™’,3yvxy{~ƒ†‡ŠŽ™k"#%3«°²´³J%$u¹²²±®³l$(%\±¬««ªª©ªª©«ª§¥¤ Ÿ ££¢¦«¯²·»¿ÂÃÅÆÅÄÃÃÂÁ¿¹´’(
+ + + + +4;;:>EGMSTWTUTVXZ]`cegfijklqswwy|†Œ
+ + +
+ + + + + + + + + +
4rj]cy‹—£ª¯±²³´´·¹¸¸···¶µ´³³´³³´³±¯¬©§¢ž˜•‘‡…‚}||‚s0 #%)+&J¤ÄºÁ_.;;<:7ż»®”vVGC?<>BD91541)I¯¦Ÿ˜‘"E}wz{|€„…ˆŠ‘–œo#%%(/–¯¯°²²¹•-<«²²³±¯µz(*)#P±®®««©©©¨§¨¦¤¢žŸ¡¢¤¦§«®³·½ÀÂÅÆÇÅÄÄÄÄÃÀ¼·¦L
+ + + + + + + + +#5435;=AEJNPQSQSVWZ^_cdddinsuw{†‡Š‘
+ + + + + + + + + + + + + + + + +9qj^`mƒ“ ¦«®¯±³µ¸¹º¹¸¸¸¸¸µ´³´³µ¶´´±¯«§¢žš—”Žˆ…~}‚}L"#&f²¿·À-68<<5}ô³³´¹¾»º·´²³¸À–151-,$‡¤—Œy%5/-/...--..(o ¦{!''((±°°³´³»n{º³´³°¯²*(("A¯±±±®«ªª¨¨©§¥ ¢¢¤¤§¬®²·¼ÀÄÅÆÇÆÅÃÄÄÄÂÀ»²u
+ + + + + + + + +%0147:;=BHLOSSQRUW[\_cfhlmtx|€„‡’–
+ +
+ + + + + + + + + + + +Csk^]f|‘𣍫®°³¶¸¸ºº···¹¹¸¶µµµµ¶·µ³²°®©£Ÿ™”ŽŠ‡‚~ƒuF<е²±¸«>388;4ZÁ¹²²²²³²¶¾ÃÇÉÊÇÆÂN*-*(LŸŠˆp hª¨ˆ*++,*†µ²²´µ´µµµµ¶µ²±°³˜0))$:©´³²±¯¬¬ª©©©¦¢¡£¤¤¥¦©¯´·¼¿ÃÅÆÆÆÆÆÄÄÄÂÀ¼¹@ + + + + + + + + + + + +
,2269:<@DIMPSTRVW[^bfimqvyƒ…ŠŽ’•“•
+ +
+ + + + + + + + + + + + + + + + +Gvm_]aoˆ•ž£§¬®±´··¸¸¶µµ¶·¶µ³´¶´µµ´´´³±¯ª¦£ž›—“މ†…‚‚…‡‰xP/ ,Gz¡«ª«®´x4;864<¨À³±®¬°¶¿ÅÅÆÅÃÀÄ‚#)($ %‡……b #j¬¬‘1.-.+~º³µ¶¶¶µ¸º¸¸µ²±°´¢6)*)5Ÿ´²³²±¯¬¬«¨¤£¤¤¦¦¦©®µº¾ÀÂÅÇÆÆÆÆÅÄÄÄÁ¾¶©w*
+ + + + + + + + +/48;>BBFLPRUWWY[`dhmty~‡ŒŒ‘†t
+ + + + + + + + + + + +Rwp`\af{™ ¤ª®³¶··¶·¸µµ¶¶µ´´´´³²³³³²°®¬©§¥¡ž›—”‘Ї‡ˆ‡ŒŠ‚ztt}£££§ª®²§¤¦ š–ž¾¸µ¬©©©«³¾ÅÈÇÆÄ¿¼¹§/$$ X…‡T $d®™5,-,-s¸³µ¶¶··¸¹¹·´²±±³¨<(++.“µ²²°°°¯¯®¬§¥¤¤¥¦§©¬´º¾ÂÅÇÈÉÉÇÅÆÅÅÄþº¶‰c'
+ + + + + + + + + + + + 6:<AEFJORVZ]]abfnruz‚…‡yj]E82
+ + + + + + + + + + + +
a{veY^`qˆ˜Ÿ¦ªª¬±µ¶¸¸¸¹¸¸¸··¶¶µ´³´´³²±¯®¬««©©§¤¢œ›—’Šˆ‡†‚ƒ‡‰ˆ‹‘“šž¢¤ª¯µ¸º¼¿Á¿¸¶²¬ª©µ¾ÇÉÇÆÂ¼¶²©®Q/…ƒ…C #]²¯¤8*(,-g¸¶·¸····¸¹¸·´³³´´M,0.)ˆ´¯®®®®°¯¬©¦¥¥¦§§©¬±¹¿ÄÅÇÈÈÇÇÅÅÅÅÄÿº¶£lg$
+ + + + + + + + /:AEILNVZ[]a`chmknkeYI?2(&&'(
+ + + + + + + + + + +
gywkZ]ae€–¤§ª«°´µ·¹»»º¸¸¸¸¶¶¶¶¶¶µ³²²±®®®®¬«©©©¥ ž™˜•‘ŽŠˆ…ƒ†‹ŒŽ”šš ¥©®®±³¸¹¹¶±ª¨¯»ÃÇÆÅÁº²«¨£¤t*& !p…ƒ< !"#""$'+//447;BDy¸´²„}„Ž’¥·····¶¶¹ºº¸·´³³´¸¥˜˜’‘¨¯¯®®®¬©¦¦¦§©ª©®±¹¿ÄÆÈÇÈÇÅÅÆÆÅÅÂÀº±df
+ + + + + + + + + + +
$*3:<CFHNNOKFD<4)+&"%$&'&&
+
+ + + + + + + + + + + + + kwwj\^adrŒ™Ÿ£¨«²´´·º»º¸¸¸¸·¶¶µ¶µ´´³³³³³²²¯¬®§¥¢Ÿœ™•‘І„€€‚ƒƒˆŽ“•𠤦¨«²·¸¸·³®«ª±¸¼»ºµ°«§¢–”†{utr}}tw{{~ƒ‡——™¡§±¸¶¶·½º¼½½»¹¹¸¸º¹¸º¼¹¶´´¶µ¶´·¸¸µ¶°¯¯®®¯«§¦¦¨ª¬®°¶¹¿ÄÇÈÈÈÇÆÆÆÈÇÅÃÁ¼±©”cjb
+ + + + + + + + + + + $$!"!# """$%$"
+ + + + + + + + + + +
"rzyo^_aci|“™ £§¬±³µµ¶¸¹ºº»»º¸·¶µ³²²³³²²²±±°®¯¯°¯®ª¦¢ž™’Žˆ‡…ƒ€€„‰Œ‘”™Ÿ¢§¬²µ´´·µ²±²±²±¯¯©¡—”‘ŽŒŠˆ†‰‹‰‚€ƒ‡‘Ž“”—™Ÿ¤¦¦§ª¯²µ¹ººººº»¼¼»»¼»¹¸¹¸¸º¸¶µ¶¶¶·¶²³²°°¯®®«©¨ª«°µº¾ÃÆÉÉÈÈÇÇÇÆÆÅÄÀ¾º¯¦šmbpa
+ + + + + + + + + + + + + !###$rbTE1
+ + + + + + + + + + + + +
%tz{r\]^acm‡–›¡¤©¯³¶··¸¹ºº¼¼¼»¹·µ´²³´³±±±²²²±°±°¯ª¨¦£¢žš•ŽŒˆ†„‚„…†ˆŒ“˜Ÿ¦¬®®¯²µ´´³²³±ª¦Ÿ™’ŒŠˆ‡ˆ‰‡„‚„†„„‡Œ‘‘’˜œ¡¤¥¦«®¯°²µº½¼¼¼¼½½½½½½½»º¹¹·´´µ´´¶µµµ³±´´²±¯«¬«««¬¬®²µ¸¼ÀÅÉËËÊÈÈÇÇÇÇÇÄ»µ®¤›zagpa + + + + + + + + + +
#%%'œ›š–“‡|dF1
+ + + + + + + + + + + + +
.y}~s^[^adg~‘šŸ£©°²´¸ºº»»º¼¼¼¼¼º¸µ²´µ´²²±±²²²²²¯®®¬ªª©§¤¢—“Ї…†‡‡‰ŒŽ’”™¡¥§«°³¶´µµ··³®¥ —•‘Ž‹ˆ‡‡‡‡……†ˆ†„ˆŠ“•—œž¢¥§¨©¬¯±²³¶¹»¼¼¼¼½½½¼»»»º¹¸¸·µ´²²±±³²²²²²²³±°¯¬«««ª¯¯°´¹½ÂÆÈËËËÊÈÈÇÈÈÇÅÃÁ»´¯¦œ€adjs]
+ + + + + + + + + + + +
"#'()•–”’“”˜™™’„j9
+ + + + + + + + + + + +
1}~~ud[]`ddoˆ˜Ÿ£¨®²´·¹º»½¼½½¼¼½»¹º¹¶³²²°¯¯±²°±±¯®¯¯¯ª¨¥¢Ÿ›–’Їˆˆ‰ŠŒŽŽ—¢¦«°²³·¹º¼¹´£–•”‘ŽŒˆˆˆŠŒ‹ˆ‹‘”˜—›Ÿ¤¦¨¨ª¯²´µ´¸º»¼¼»»¼½½»º¹·¸·µµµµµ³³±®®¯®®®®®¬®®®¬«¬®¯²´¶¸½ÀÅÉËÍËÉÊÈÈÉÊÈÇÄÁ¾¸³§œŒi_flu\
+ + + + + + + + + + +
#&()*‘”““‘’“’’“—k
+ + + + + + + + + + +3€€zd[^_bdfuŽ˜¥«¯²´¸ºº¼¿¿¾½¾½»ºº»¸´²±°¯®¯°¯°±³²°¯®®®®¬«©§¤ œ™”ŽŠ‰ˆˆ‡ŠŒ‹“˜œ ¤©¯³¸¼½À¾¼¸®«¤¡Ÿ›š——“Ž’’’‘— ¡¢¥¨¬®®¯°³´·¸º¾¿¾½¼¹»ººº¸··¶¶µ³³³³³°°¬««¬¬«ª«««®¯®¯±²³´¸»¾ÃÆÉÊËËÊÊÉÉÉÊÊÉÅ¿»¶²¬¨¡•r\cimq] + + + + + + + + + + + +!$#%%)*)’’’’“““”’‘g
+ + + + + + + + + + + + + + + +<~€{h\^`cedm‚Œ˜¡¨®²´·¹º¼½¿¾½¼¼º¹º¸¸µ³³²±°°°±²³´²¯¯®®«ªª©¥¢Ÿš–•ŒŠˆ†ˆˆ‹’–› ¥¬±·»½ÀÀ¼·³²¯©¦£ ›˜œ›››Ÿ¤¦¨¯®¯±³¶¶µµ¶¶·¸»¾½»»º¹¹¸¹·¶¶¶´µ´´´³²°®«ª«««¬¬¬¬®®¯¯°³µ¹»¾ÁÃÆÊËËÌËËÉÊÊÉÊÊÈÅÁ¾»·±¯¨¦Ÿacgknq\ + + + + + + + + + + + "#%'&&&*+)“’’’’’’’‘Ž’S
+ + + + + + + + + + + + + +
>ƒ‚{l_cccedgz‰”Ÿ¨¯²´¶¸º»¼½½¼¼¼¼¼»¹¹·µ¶µ³±±±²²³²²²°¯®®¬«©§¦£¡Ÿš—‘Їˆ†ˆˆ‰Œ•¢§¬³·»¾¿ÂÆÆÄ½»¸±°ªª¨©ª¯°²¶¸¸º»º¹º»¼¹¸··»½¼¼¼»»º·¶µµµ¶³´´µ´²±¯®®®°±°°²²´³³µ¸»ÁÃÆÉËÌÎÌËÌÊËËËËÊÈÆÃÀ»·³°©£¢˜nadgioo` + + + + + + +
$&&$&(&)*(’’‘‘’ŒG
+ + + + + + + + + + + + + + + +
A‰†~o`bcgihgo‚™¤ª²´µ·¹¹»½¾½½½¾½¼¼»¹···µ³²²±°¯¯¯±°°°±±±²±°¬ª©¦£¢ŸŸ˜•’Ž‹‡†…‡‡ŠŽ“˜¦¯¶¹½ÀÄÄÆÈÈÅÄÁÀ¼º¹¹·¹ºººº»¼¾ÀÃÁÀÀ¾¿¿¾¼ºº»½¾½¼½¼¹¹¶µ´´µµ³´´´´²±°°°±°±±±²³³µ¶¸¸¸¹¼¾ÃÇÉËÌÌÌÌËÊËËËÊÊÈÇÄÁ»¸µ²®¬ª¨¡ž€cbehlrmb" + + + + + + + + +
#&$$'&&())‘Ž‘ŽŽŽŒ‰J
+ + + + + + + + + + + + + + + +
KŒ†ƒ~saachkkkkv…’𤱴µ·¸»¾¾½¼¾¾¾½¼¹····¶µ³²²±±¯®°²²±²³²²³±¯¬ª©¨¦¥¡ž™”Œ‰ˆˆ‰‰Š“œ£«±¸»ÀÇÄÄÅÆÆÆÇÅÃÂÃÄÅÆÅÄÄÅÆÅÆÆÅÄÃÃÃÃÁ¿¾½¾¾¾¼¼¾¼»º¸··¶¶µµ¶´µ¶³°°±²²²µµ¶¶·¸¹º½ÀÂÃÅÇÊËËÌÌËÊÊÊÊËÊÉÉÆÅÿ¼¸´±ª©¨¤™Šjcfikmund# + + + + + + + + + + + + + +
"#%$%'(''()ŽŒŒ‹ŠŠ‹J
+ + + + + + + + + + + + +T‹‡†‚va^chjlmklx‡•Ÿ«°°²µ·¹½¾¼½¾¾½½»¸¶¶¶¶¶´³³³³³²±²²²²±°±±²´³³²°¯®¬ª¦¢™•Љ‹Œ‘–ž£¤OHuÆÁÂÃÄÅÅÆÎÑÐÑÊÊÉÈÈËÒÑÏÈÅÉÎÌÍÎËÉÊËÆÀ¿¾¾¿¾¾½»¹¹¸·¶¶·¶·µ³±±³³´¶¹º¹º¼½¿ÃÅÈÊËÍÍÍÌÊËËÊÉÊÊÊÉÉÇÄ¿º¶³¯¬ª¨§¥¢œŽthfiikntmf$ + + + + + + + + +
!!"$$$'&&()(ŽŽ‹Š‹Šˆ‡‡‰D
+ + + + + + + + + + + +
^Љ„|b`cfklllgo“§®±´¶¸¹»¾¿¾¼½¼¼»¹¸·µµ¶µ¶´´µ´´µ³´µ³´´´µµ´µµµµ¶´±°¯¯¬ª¤ œ˜—–‘ŽŽ”šŸ¯e ,,žÅ¾ÂÄÄÄÄ~hl˜ÑÊÉÇɽr’ÊÉjhfdbgrŒ«ÇÉÂÁÁÀ¿¿½»¼»º¹¹¹¸·¶µ´´¶·¸¹¼¿ÁÂÄÅÇÉÊÌÍÍÌËËÊÊÊËÊÉÉÈÇÅ¿½¹·´¯ª§¦§§¥Ÿ–‚qiikjkqtlh+ + + + + + + + + "#$%%$#'&&‹Ž‹ŒŠ‡†ˆ†…ˆE
+ + + + + + +
d•Ž‹‡jbeghllmjjx‹™¢©°µ¶·º»½¾¾»»½¾¼¹¹¸·¸¶µ¶·¸¸·µ···¹¹¹¹¹¸¹¸·¸¸¸···¶µ³³±¯ª§¢žš˜•‘‘‘’”œ–*%,)\Ä¿ÀÄÃÃÇ\)//}ÐÈÅɼE.MÃÌ«312:>;5.6^¡ÍÇÄÅÄÄÄÃÃÁÀ¾¼¼»ºº¹»»¼¾½ÀÄÅÇËÌËËÍÍÌÍËÊÉÉÈÈÉÉÉÊÈÆÂ¿»¸µ³±®«ª©¨¨§¥ ›xmjkkknuvml/ + + + + + + + + + + + + + + + !"#$$$%&&&(ˆ‰ŠŠ‰‡…‡‡„ƒC
+ + + + + + + + + + + +k•Žˆodfgikklkjp} §¯´¶¸º¼½¾½»¼½½»»º¹¸¸·µµ¸¹¹¹¸¹º»¼½½¼½¼½¼»»»»º»¼»¹·¶µ´°®«¦¡ž™••–•‘œa%$**•Áº¾¾¿Çy076,nÉÈÃÇ]6?±È»H6G«º·³–b-3vÊÉÆÇÈÇÇÇÆÆÆÅÃÁÂÂÁÁÂÄÅÅÈÊÊËÍÍËÊÌËÊËÊÉÈÈÇÇÆÆÇÆÄÀ»·³°ª©©§¨§¨¨¤¡œ“‚qlilklqwxnk1 + + + + + + + + + + + + + +"#$#$%&&((‰‰‰Šˆ†„…„ƒ„? +
+ + + + + + +o–‘މ‚qcceggijjiiuˆ˜£²µ¸¼¾À¿À¿¿¿¾¼»»º¸¶´³µ¸ºº¹»½¿¿¾¿ÁÁÁÀ¿¾¿¿¿ÀÀ¿¿½»º¸¸¸¶³²¯ª¦£ž›š˜–‘/!&!L´²¶¸»Ä2622,kÌÄÉn36›ÆÅ[1A¹ËÈÉÍÎ’8+eÉÉÇÉÈÉÉÈÉÉÉÈÇÈÉÈÇÈÉÊÊÊÊÌÌÌÌÌÊÊÊÊÊÉÈÉÇÆÅÃÂÁ¿»·±®ª¥¥¥¦¢¢¤¥¨¨¥Ÿš‘{mllnlnt{|pp5 + + + + + + + + + + + + + + + #$##$%$'('ˆˆ‡‡‡„ƒ„ƒ‚B
+ + + + +
u—“‘Š„vbccddfggigk~’Ÿ¨¯´º½¾¿¿ÀÁÂÁÀ¾¿½½º·´´¶¹»¼ºº¼¾¿¿ÀÁÁÀÀ¿¿¿¿ÁÂÁÀ¿¿¼½¼»¼¼º¹¶²¬¨£ ž˜œj.r!†¯¬²³¾-1RR10lÈˇ23ˆÇÈo25¢ËÅÅÄÅÐ6.ƒÏÆÇÉÉÉÉÉÊËÊÉÉÊÈÇÉÊËÊÉÊËËËÊËÉÉÊÊÉÉÇÆÂÀ½»»ºµ±©¦¥¢Ÿ ¢¤¦¨§¦Ÿ•ˆsonnnlou}|ot= + + + + + + + + + + + + + + !!"#&%%$&‡††…„‚ƒ€}}:
+ + + + + + +
!™”‘‹†{ecc`adffeeiu‰™£«±¸»¼¿ÀÀÂÄÄÃÁ¿¾¼¸··¹¼¼¼½¼ºº½½¾¿¿ÀÁÁÁÁÁÀÁÂÁÀÀÁ¿¿¾½½½½»¸µ³®¨¤ ™—7XžEA§£§¬´)+^¬<4-xÏ–32qÉɆ03‡ÎÅÇÆÄÃÅY1E¼ÉÇÈÉÈÈÈÉÈÈÇÈÈÈÇÇÈÈÉÈÈÊÊÊÉÉÉÇÇÆÆÅ¿»¸·´²³¦£ ŸŸŸœ››ž¡¤§§¦¥›“}npoonmpy€{qx? + + + + + + + + + + + + + + + +
"#$%%%&&……†ƒ~~|}4
+ + + + + + + +
%…™–‹…{hcc_`ccdeegk|ž¦¬²¶º¾¿ÁÃÃÃÃÁÀ½¹¶¶¸»¼½¿¾»»¼¼»¾¿ÀÂÂÂÂÁ¿ÀÁÂÂÂÁÁÂÁÀ¾½½¼»¶´³¯ª¥št!‚w}¢¤¬Š((RÑ/4,ª72_ÇÆœ56iÍÄÅÃÂÀÈs44™ÌÆÇÈÈÇÇÇÇÇÆÆÇÇÆÆÆÇÇÇÇÆÈÈÆÇÇÄÂÁÀ½¸µµ²²°«¥Ÿœœ™š™™šœŸ£¦¦¥ ˜soqrrpnr…}szE + + + + + + + + + + "$%$$$$$…„„‚ƒ€~}z|8
+ + +
&†–”†|hcba`abcdfgem|–¢§¯³·¹¼¾Á¿¾¾¾½»¹¶¶¸¹·ºº¹¹ºººº»½¾¾¾ÀÁÁÁÀÀÂÁÀ¿¿¿¿¿½»½»º¶³±¬¥EO”ŠŽE>˜•š ƒ%#I±¶r'-1|;2N¼À¨?7PÀÁÀ¿ÁÃÇb21”ÉÄÅÆÆÅÅÅÆÆÆÅÆÆÅÄÅÅÅÄÄÄÃÃþ¾º·´²®®«©¦§¤œ™–•”’’‘”—šŸ£¦¥¢š“€lpssrppv‚ˆ|v|P + + + + + + + + + + + + + + + + +!#$$$%&%‚„ƒ€~~|}|{yz=
+ + + + + + +
'ŠŸ›–މ‚kcbb`_``cefedn†›¥¬²µ¹»½¾¼º¸¸¶¶··¶´³³´µ²´µ´µ¶·ºº¹ºº»¾¾½½¼½»¹¸·µµ²°®¬«©§¦¤žž‡azvtYlЇv; Ÿ¦B %%*,<¯»±A2=´¾½½ÀÊŒ67>¬Á¾¾¾¾À¾¾ÀÀÀÀÁÀ¿¿¿À¿¾¾¾½½¼½¼¸µ²¯¬¨¥¡ Ÿœš•‹Šˆˆ‰Š‘•›Ÿ¢¤ œ“…roqsrqpr|†Š}z€Z + + + + + + + + + + + + + + + + + !!"%%&'%‚„€~|{zz|{xz?
+ + + + + + +
+ŽŸš–’‹ofdb``abbdefghwŒŸ§±¶»»»¼¹¶µ³³µ·¶³±®¯±²³³¶¶¶·¸¸·³°°°¯«©¨¤¢¡žœ››˜–Žˆ‘D*|y~m-“’Ÿˆ# ##%1¥´±E-.¢¾¸»²0-&c³¯³±±³³³´³²±²³³²²²³³³²²°°¯©¤¡Ÿžœœ˜—•’Š…ƒ~€ƒ†‰”™Ÿ¤¦¦£™Švmortrqqu€‹‹}~…d + + + + +
+ + + + + "%%%&'€}}|z{{zwv<
+ + + + + + + +
2‘𗓆thfdba`abdefhej“¢¨¯³¸¹¹¹¸¸¸¶¶¸¸¸´±®¨§¥¤£¥§©§©««««©«««§¢¢¡Ÿžœš™•’Š‹Šˆ……~pKyte*„ƒ„’c#Š¡ =^sfT80„–‘“—™››œœž £¢¡£¢£¥¤¤£¡ Ÿžš——”‹ˆ…„{{}€‚„‹•šŸ§««¨¢”‚qnprsrqsx„ŽŠ{€†g + + + + + + + #$$&&'}}~€|{z{xywuC
+ + + + +
+
2’ž›˜’ˆ{jjhda__abfehhfr‰¦°´·¸¹¹º»½¼¼½½¾»¶²ª¤¡žœžœ›ž ¢¡ Ÿ ž—ŒŠ‹†‡„ƒ{|{vxzussrxB$T[]\]^^L!Zja nsrsx?]rx1">gonsz{ƒ††‹‹Ž‘‘“”—™›žœœœš—”‘Ž‹ˆ…~}{|z{€ƒ‡”—ž¢¦®¬¦šŠtmorssrru|‡”‹}ƒ…d
+ + + + + + + !###'('}}~}|zxwuusuE
+ + + +
2”Ÿš“‰{mllgcb`abachiho|‘¡¬²¶¸»¼½¿¿¾ÀÁÀÀ¿º´°©¢›š˜™š››››˜–“‹{yyywvtttrrqoolkje<59[kiiffffj]EC@\e@9:_iihhkI+*)Vfg?,1449AIS_hegjmqru{|y{z|€€„‰“—šœš˜–‘ŽŠ†„~{yz|}~„Š˜›Ÿ¤©¬¯®©¡“|mnpttssrv€Ž•‡€‡Œh + + + + + + + + + +!##$')){|}|{yxturquP +
+ + + + + +
7— ž›–Šqqnleb_abcceikio‡œ§®´¶º½¿ÀÀÁÃľºµ±«¦¥¡ž›˜˜™š˜™˜”‘Œ„zvtutsrqrqnmmkhgea^]``^]^[^]^``aaa`^^_`cadeddcfcbb```_^`baaaababdfiknoqtutuuwzzz„Š‘•šœœœš—”‘ŽŠ‡„€~|zz|~‡Ž’™ž¡¦ª«°°¦›smpqtuussy‡””„‚Šo
+ + + + + + + + + #$$&(+|zy{ywttssorR +
+ + + + + + + + + +
9›¡ ˜’‹ƒvtumhea`bdcehjhiyœ§¯³·½¿ÀÀÃÄÃÂÿ½¹µ¯¬¨¡ž›š›œ›™—“Œ„}wrrsqoponjijffggecdddefecddefgeddcbdcddcdedccabb````^^]^`^^_aaddeilmoopqqruwy{}€ƒ…ˆ–ššœš—–‘ŽŠ‰ˆ†‚~{|}}„ˆ‘”™ž£¦«¯²¯£”~lnpsstuuv–”„…Ž’u + + +!#%%'+{{z}vvtssrpmI
+ + + + +
+
>¢¡˜“Œ†zuwrnhdabdcehiihmƒ”œ¦®µ»¾ÁÂÄÅÄÂÂÀÀ¿¾¹³¯¬§£¡Ÿž›™˜–’І€{wvrppommlnmmmoononnmmopmlmnmjjjhfghhhhghifefeddedbab```a`bccdfdejnpomoprsw{}~€ƒ‡Š”›žœ˜•“‹ˆ†…ƒ~}|~‚‰‘—œ ¥ª«®¯³²°¬žŒomprtutuvyƒ’™•‡’–x +
+ + + + + + + + + +0!$%&&+yyzyuvssqpnnP
+ + + + +
BŸ¡¢ž™”‰}u{yrmgcbcdegjjlku…–¢©±·»ÀÂÄÂÂÁÀÀÁÂÁ»·´°¬¨¥££¡šš–’Œˆƒ|zwttrtvwxwwvvwvvuvtrqsrqpqqpponmmmnlmkjjikihghhgfddcccdcdeefghjmoponqsw{~‚ƒ†‹ŽŽ•š¡¡žœ˜”‰‡…‚€|}}„„Š”™ž£§ª¯±°±´²®¥–zkoqstuuuv~Š—œ”‰•˜y + + + + + + + + * "#&&)+wxwvttrrollkW
+ + + +
J¡¢£ž›•މ€uz{uqleabcdfijkkm{™£¬³¸¼¿¿ÀÀÁÀÀÃý¹·³°«©¥¢ Ÿž›—”“ŒˆŠ‡‡„€€€„‡ˆ……‚‚‚€~|{yzxwxwuuttttrrrssrqnmmonmlllkihhhhiiikikmnpqsusrvz‚ˆ‰‘““•˜š›Ÿ¢¢ ™–‘ŽŠˆ†ƒ~|€‚†‰Ž–œŸ£¨«®±´´´´²«‰qmosutuvvz‚‘›Š“—šz
+ + +
+ + & "#&()+-ywttrrronlkhb + +
+
N¢¢£Ÿ›•‹„vw|yuqkcbccegikkkr‘§®´¹»¾ÀÀÁÂÂÃÄÄ¿½º¶²®¬§¤¡ œ˜–”“‘“Ž“šŸœœ™˜™—•‘‹‹Š…„‚~}}|zzz{|zwvuvuvvuqsqqppnpqosttwy{ƒƒ„†ˆ–———˜›œžžŸ¢£¢Ÿœ˜“Žˆ…„ƒ€}{~‚†Š•›¡¦©«¯²µ¶··³£‘tnoptvvwxw|ˆ˜œ‹Œ–˜{
+ + + +
+
+ + + + + !' #&*)*+/ttrsrpmllkjfd
+ +
+
P¦£¥¡›•ˆxx~|ysngabbdghjlmox‹— ª¯µ¹»½ÀÂÃÃÄÅÅÁÀ¼¹¶±®ª¦ œ™–””“‘”••˜™£ª±´´²±®ªª§¥¥¦¤¦¥¤¡ž›œ™˜•””’‘“‘Žˆ†…ƒ‚€€€~~€‚ƒ‚‚„††‡ŠŽ‘™Ÿ¡¡ ¢£¤¤¥¦£¢£¥¤ œ—“Žˆ„~~|}ƒ‡Œ‘—œ ¦©¬¯³¶¹¸¹¸±§œ„lnqswvwyzzƒ›Ÿš‡™››
+
+ + + + + + #'#&*,-.13trsqonljjihec
+ +
+ +
N§¦¨¥ž˜“‰}v|~zwqkdaccdgjkkmr}™ ª²·º½ÁÄÃÄÆÇÅÃÃÀ¾»·²®¨¢™–“’“‘’“’—ž¤©±·»»»»¸³±±´µµ¶··¸·´²±°°®¬¬ª§¥¦§¦¥¢ž›š™—•”””•–“’’–—™šš™™š›œ›› ¢¢¥©««ªª«¬«©¨¦¦¡™‘‰„||}~ƒ†‹’˜£§ª±µ¸¹¹··³«ŸŠtmoquwuvyz‰•ž¡–„‘™›
+
+ + +$&!'*-.035sqrpmlkhhgddd + + + + + +
M¨¦¨¥ œ–‘‹€x|~~{umhbcbefijjlpvƒ˜¢¬´º½ÀÃÄÅÇÈÅÄÄÄ¿º¸µ¯¨¢˜”“‘“•žª±·¼¿À¼»¸¶º¼¾ÀÁ¿ÁÃÄÃÃÃÃÀ¾¾¼½¼º¶¶¸·¶¶²±®«ª©§¦§©¨¦¦§¨©¯±³¯°°¯¬¬¯±²³³²±²²´´³²¯¬¨¨¥ –Š„€~~|}~ƒ†‹“™¤©¬®²¶¹ººº¶²£–ynopsvwwwy|‚œ¢¢‘†•œœž
+ + + + + + + + + &&"$+/0246pnqmmkffffcac(
+ + + +
+ + + +
M§¦§¤ ž™•…y|~€{tneabdfgjjkou{‡”ž¦®¶»½ÂÆÆÇÇÄÄÇÆÅÃÀ½»µ°¬¦¢›™•’“™¡¨±º¿À½»»¾±±ª§§¥¥ž—˜—–”—¥·ÂÄÀ½¾¿¿¿¿¼»¼¼»º¸··¸º¸·¶¹¹½¸ª˜‡„Œ•£´¿½»»»»½½ºº»»»¹¸·³°®¬¥œ‘Š…€~}„…‰”˜Ÿ¤¨´¶·»¼¼»¹´°§œ†nmpsvywvy|‰– §£‹†–Ÿ ¡~ + + + + + + + + + +)(#!#(/4567mmlhkjgeddcb`3 + + + + + +
S¤¨©¦¡™–’Šzy€ƒ€}xslb`bdfgjlnqv~Š“¥®¶»¿ÅÆÇÆÅÆÈÈÈÇÅÁÂÀ¼¸³¯«¨¦£¥ª²¹¼ÁÀ¿½»»¾¼RA=;;<;98<7476=K\¯ÆÄÀÀÀÀ¾¼½¾¾¾¾¾½½½¼»ÀÁ¨~X?320,.39KpžÂÇÀÀÂÃÄÃÂÁ¿½¼º¸¶´¯¥™„~}€†‹”™ ¦ª®³¸º¼¾¾½»¹²¬ tmmpuxywv{„›¤§¡ˆ‹™¡¡£z
+
+ + + + + +((&!#*46889jliihhfccc`\[9 + + +
W¨ªªª¥Ÿ›™•‹~w€ƒ€{tpgba`cgijnoru}ˆ“𤝵¼ÁÃÆÆÅÆÈÉÉÈÇÇÈÇÄ¿»¹¶µ´µ¶ºÀ¿¾¼º¹¹¹»±?9:99::8;:6779;;5.@z»Æ¿½¾½¼½¾½¾¿ÀÀ¾½¿Á¢f:+01/430111,*7VŒ½ÆÀÁÁÀÀ¾½¼»¸·µ²ªœˆ€ƒŠ‘—›£§±¶º½¿ÀÀÀ¾º¶¡–}lnptwzxxyƒ‰–¡©©›ƒœ¡¡¢w
+ + + + + + + +%)'"!!%17::<jhffgdd`a_[WU=
+ + + + +
[§©¬«§¡š–Žƒw~ƒ‚}wtjdb_`eginnprwŽ– ª³»¾ÂÄÄÃÄÅÈÉÈÇÈÉÉÈÆÅ¾¿¼¼½¾¿½»ºº¹¸¸¸»§63446776546::978874/JœÇ¼¼¼¼¼¼½½¾¿½ºÃµo6)021../210013793/M›Ãº¸¸¶´²°¯¬ª¤›ˆ„„ƒ‚…’•𠦬³µ¸»¾ÀÁÀÀ¾º´®¥•„topsvzxwz}€ˆœ¦«©•’Ÿ£££x
+ + + + + + + + + + + + + ((% "%.6;=@gdcda``\XXVRQD
+ + + + +
]¨©¬«§¤¡›•’†y~ƒ„ƒ€|wrhb_^`eilnnqtz„™£ª´¸¾ÂÂÂÄÆÉÊÉÊÊËËÊÉÇÅÄÅÄÂÁÀ½¼»º»º··¸º0...0.0012/41,/3345731‰Æº»¼ºº¼½»»¸Á¥L///00010//./0/04421+1y»·²±«¨¤¢žœ–†ƒƒƒ‚‚…‹‘”™Ÿ¥¬¯´¹½¾¿Á¿½¼¹µ¯©›‰wonrsy|yxz…‹–¡ª¬©‘ƒ–¤¦¤§p
+ + %R6%' !$%*17<>aa`_]^]WVVSRPJ + + + +
cª©¬¬ª§¢Ÿš“ˆ}}„………zunfa^^aejlloru{„–Ÿª·¼ÀÁÂÆÉÊËËÌÎÍÌËÊÊÊÉÉÇÅÃÀ¿¾½½¼¹¶¶µµ•+)))$PŒŒ‘‘„pJ.(10/311’ø¹º»»¼»¹ÂŸ?0111/00),044.++2530...(k¸±®ª§£ š•‹ˆƒ€€‚…‰”šž£©¯´¹½¾¿¿¿¾½º¶±ª‹{opqtw{{{z}„‰œ¦®¨‰‡š¤¦¥¥j
+ + + 3M3$$"%%%,38:___\[XXSSUQPLE
+ + +
c¬©®¬¨£Ÿ›–Œ~ƒ‡‡†‚}xslea_``ehjloqv{…‹•Ÿ®·½ÀÄÈÊÊËËÍÎÍÌÌÌËÌËÈÇÈÆÃÀ¿¾¾¹·µ±¯®'&$%q³®±²µ²²´²“K&-,..1;§»µ¹¹¹»ºÂ¥=23/11,);]™ ¡šŒmG--0,,.,"q¯£¢ œ˜”ˆ„‚€‚…ˆŽ”›Ÿ¤ª°µº¿¿ÀÁÁÀ¾¼¹³«¢‘soqswz|{{|€‡Ž• ª¯®¤‰ž¦¨¦¨f + + + + + + + + + + + + +#(1G2$%!$%'-138]\[XWVTRQPMLHB + + + + +
e«®°®ª¥ ˜‚~ƒŠŒ‡†‚|uphebb`bfhjlnsu{„Š‘ž«¹½ÃÈÊÊËËËÊÌÌÌÌËÊÊÈÈÊÈÆÃ¿¼¹µ³²°®¯Ž#$#$ p²¯°±°®²ºµc'-,--'Y¸´µ¶¶¸º¹L010.0++k§½¿ºº½¿¿Ã´:&,)&##$…¢™™•‘Їƒ€‚„†‰Ž•›¡¥©®´¸¾¿ÀÂÃÃÁ¾»·¦›‡vopquy||||„Š‘™¥±®˜Ž¢©¨¥¨^ +
+ + + + + + + + + + +&)*-@/!#"$%+115ZXVRRSROMLHDEB! + +
+
j««°°¯¬§¢žœ“‡€‰Œ‹ˆ†€xsmhfdcbahikmqruy€†Ž™¬·¼ÁÄÆÇÉÊÊÊÊÉÊÊÉÊÊÈÊÉǾ»¹¶µ´±®²#$#%#t´°³µ³°°´´·»]'.+,+-“º³³²³¼q,/-,.(5‹»¶´´¶¸ºººº»Ä«P"&!"?˜““ŒŠˆ†…ƒ„…†‰Ž•¡¦«¯³¸¼ÀÁÁÃÄ¿»·²ª|ooruxz|{|~ƒ‡Œ•Ÿ©°±«“£©¨§§W
+
+ + + + + + + + +&())/A,"$!%%)034VSQONOLJIIFEC? + + + +
+
fª¬±²°®¨£ ž–ˆ€ˆŒŒ‰‚~wpmifcbaaeimoostzƒˆš©´¼ÀÃÅÇÈÊÊÉÊÉÈÈÉÉÈÈÈÅÂÀ½º·¶¶²±²Œ$&#$#p³±³´³´´µ¶´·>'++,&]º±²±´Ÿ4**)+(5𷬮°±²´´³´´´²¶±Lz•Œ‹‰†…„…‡†ˆŠ”œ¡¥«¯³¹½ÁÂÃÄÄľº±ªžrmoswy}}||€…‹’›¤®±±¨†–¤¨¨§¥S
+ + + + + + +((()(+7*$ $%'/43SSOMLKLIFEDC?=& +
k«¬²³±®«§£Ÿ™Š€†ŒŽŽˆ„|vrkeddcaafklnqru}ƒŠ¬¶º½ÀÃÆÈÉÉÊÊÉÈÈÈÈÈÇÆÆÃÀ½¸¶µ´±³Ž$%$%$q²®²³´³´´³µµ³¹„'-+,*7§´²±·j#)(')&†¶«®¯¯°±±°°°°±®©ª›1P‘‰‡‡…„ƒ†‰Š”𡦩²·»¿ÂÅÅÅÅÃÀ»µ«ž“ƒspoqvy{~~|~‚‰— ©¯±°¡}„›¥ªª©§N
+ + + + +
))()))+1*"!"'/35QOMJJFGECAAA><. + +
p«²²°¯ª¦ œ€€ˆ‘‘Ž‹ˆ‚|wqiggfbbbeimpqsuzˆŽ—£²¹¾ÂÄÆÈÈÉÉÈÇÇÈÈÇÈÉÉļ¹¶µ±³Œ$%$%#t³®°²³´´µ´³³²³®;++*+*‰¶®±@"&&&!X°««¬¬°±°¯¯®¬«§¤Ÿ£g4‡…„†…„…ˆŠ‹‘–š ¦ª¯¶¹¼¿ÂÅÇÅÆÂ¿»µ¬Ÿ•voprux{}~~|…Œ”œ¤±´¯—|Œž§ª«§£E
+
+ + + + +
*)(()((*3*#!#-44MIGGHED@?@?=>:/ + + + +
r¨¬²³±°«§£Ÿ˜‡‡Œ‘’Š…€{vojggeda`fhlnorsy~~‡‘¡³¸¼ÀÃÅÅÇÈÈÇÈÉÈÇÇÈÉÇÅ¿½ºµµŒ$&&(%w¶±²µ¶¶·¹¸¶µµ´»Z$))+&m¹¯´—+&$$$+”°ª¬®¯±²±°¯®¬ª¨¤£Ÿš‰''€………„†ˆŠŽ‘”£¦¬±µ»ÀÃÅÆÇÇÅÄ¿»¶¥•„uonqtwy}~~}~ƒŠ– ©¯³²«Ž‘¢«®¬¦=
+ + + + +
#)()(()))*/-" ".33GFFDBAA?>>=<;73 + + + + +
sª«²´²¯««©¥ šŒ‚‚‹’“Œˆ†ztnihhfcbbegjmqsuv|€…‘¢«³¸½ÂÄÆÆÆÈÈÈÉÉÈÈÈÆÈÇÆÄ¿¹ºŽ%()*'~¹²²¶··¸»»¶¶µ³¹n())+'^¹²·~$'#%$Gªª«®®¯±°®¬¬ª¨¥¢ ™‘’;'|‡ˆ‡ˆ‰‹Ž‘•œ¡§¬´¹½ÁÆÈÊÊÉÇÇÀ¼º²§™†wnmptx{}~}}‡Œ“›¤²µ²¦…–¨¯¯¬§›;
+ + + + + + +#')(((*((''2- '/2GCAA?;<<=;<;876 + + + +
s¬²´´±«ª¦£œ…‚Š“”’‘Œ‡„yrmjihgebcejmprruz|€„‘ «²·½ÀÂÅÆÅÅÆÆÇÇÈÉÉÉÇÅÄ¿¼¿(-+,+‚¸³µ···¸¸·¶´³²·z&)')'U±«±j $#%!_«¥©©©ªªªªª§¦¤¢¡™•‘“G%}Š‹‰ŒŽ”˜œ£§¬³º½ÂÇÈÉÉÈÆÃ¿»·°§›‹|tkortx{~~~~„‹— ©±¶¶±Ÿ~‡›©®®¬¨›4
+ + + + + +
$(('(')('''(0-$,/DB>=<988987875/ + + + + + + +
oª«²µ´³°©§¦ –‡„Š’••”’‰†‚~wrnkjihfdegimppruxz{Š›¨´¹½ÀÂÄÄÅÅÆÆÈÉÉÈÆÄÄÃÁ¾Å‰-/+-.Џµ¶··¸·µµ¶¶´²¹%)&(%M®¨°W!"% o©¤¨¨©©ª©ª©¦¤¢¡Ÿš–”Ž“I+…Ž‘•—› ¥«¯·½¿ÂÆÉÈÈÇÄÄÁ½¸±¨Œ}ummptw{~€~~‡Ž”›£®³·´¯˜|Œ©®©˜, +
+
+
+ + + + +&('''%&&&'''(/+(,A?<<;999844543- + + + + +
{©¬±´´³±®«¨©¦™‰„Š’˜˜˜—”‰†‚{upnllkjhgfgkmpqqtvxz„•¡«±·¼ÂÄÄÄÅÅÅÆÇÇÆÅÄÄÄÃÂÈ|-210/–¾¸¹¸¸¹¹¹¸¶¶µ²ºs%&$&#P¬¤ªP #"«§©«©««ª©¨¥£¢¡ž›–“’E3’“••˜œŸ£¦¬±¶¾ÁÃÅÇÈÈÇÅÂÁ¿»´«¢’ulmrtv{}~€€€„Š’™ §°¶¸³©‡{’ ª¯¯¯©'
+ + + + + + + + +(''&&'&&'&$%&%/0%*<>>=9866543420/ + + +
0ާ®²³´³°¯ª©§„ˆ•˜šš—’Œ‰„ztollllljgghknqqrtuwyzœ¦±¼¾ÁÁÂÃÃÃÄÄÅÆÄÆÆÆÄÃÉo59:7:¦Áººººº»º¹·¶´±·Z!$#% Z¬¤ªQ!xª¦§§¨©©©§¥¤¤¢Ÿ™–“‘“=?˜•—™ž¡¥¨³¹¿ÄÅÇÉÉÉÈÆÅÁ½º´¬¦—‡yrmpuwx|~€€‡Ž—ž¤«²¶·³¢‚‚–¥°²°©Ž"
+ + + + + (''''&&&&&%%%$&+-$'::::754420//.-) + + + + + + + +
T›¡³µµ´±°®«©¨¢‘……‹’•˜˜—•’Œ…‚~ytommmnmkhiilpqrsttvxy~‰—¢¯¶»¾¿ÁÁÃÃÃÃÃÅÇÆÆÃÁÅ_89>=H²¾»º¹¹¸¸·¸¹µ°¯®?$$"$i«£«Zd©¤¦¦¦¤¥¨§¦¥£Ÿœ›˜–”—Š(Wœ—œŸ£¦«¯²·¿ÆÈÉÊÊÊÈÈÅÃÀ¼³«£—†zrnpuxx|€€„‹•œ¢©¯¶¸¶²—|†š¨¯²²¯ª…
+ + + + + + + + !('&'''&%%%%%%$$$-0$776753210.-,++* +
+
{¡ ¬³µ´³²°¯®«©¢•Š…“•˜™š™•‘‹†ƒ}xsppnmnolkkknprsstuusuz‰™¦´º½¾¾ÀÁÂÀÀÅÅÄÃüO:9<?O´¼¼º¸¸··³±±¯ª«“($!!%"§ ©lF§§§§¦¥¦§¦¤£¡ž›™——•˜iu›™Ÿ£¦©´·¼ÆÉÊËËÊÉÈÇÅÀ»µ®¥˜…|tlosyzz~€€€€‚‡‘𠦳¹¹µŒ{Šž«°±²¯¦}
+ + + + + + + + + #'''''&&&%%&&%%##%+/65455300/-,+*)(! + + +
6› ¬³µµ´²±°¯¬ª¤™‹‡Œ’˜œœ˜•‘Œˆƒ~yutronopommlprrsstustvx{…—©³¹»¼¾¿¿¾½ÀÂÂÂÁ±?6324M¶»»¸¶µ´²¯¬ª¨¤®_ ! /˜£ ¥„ %ª§©¦¦§§¤£¢Ÿžš™˜–••@+’ž¢§ª¯³¹ÀÈÌÌÍÌËÊÊÉÆÃ½¶¯¤—‹~tjlstwz{‚ƒ‚ƒƒ†Ž–ž£ª²·º¹´¥„|¢±²²®¨€
+ + + + + + + + + +$('''(('&&&&%&%$%#%,353310///,*'')&$ + + + + + +
\žžª°µµ´´³°¯®«§†Š—žŸœš–‘ˆ…€|ywsqqsrpopqqrrrrstutux}ˆœ¬²µ¸»¼½¾¾ÀÁÂÂÀ¨5.+++O»½º·´²±®ª¨¤Ÿ¢ˆ$M£ž ¡™/ b°©©§§§¤¢¡Ÿžžœ›™•žjO¤ž ¦¬°µ»ÀÆÉÊËËËËËÉÆÃÀ·£—‰}ulkotwxz|„„„„†Š’š ¨°µ¹º¹±š|~”¤²³³®¬{
+
+ + + &)))'&%&&%&&$%$%&%$&+.211/---,*('&&%" + + + + +
#†´¢©¯³´´³³°°¯®«£“ˆˆ”›Ÿ Ÿœš˜”†~|xutuvvutsssttstvvuxxz€¥¬¯°³·¹º¼¾¾¿À¿ÂŸ53,,,Y½»¸´°¬ª¦£•›4z šž£U! !)а¥¥¥¤¡Ÿžžžžœ˜‰.€§¢¥²µ»ÀÅÈÊËÊËÊÊÉÇĽ¸¤˜‰umkortx{~€‚„…„†‰Ž–¤«±¶ºº·¬{†˜¨°³³²®¨
+ + + + + +&***'%&&%%%&%%%$&(&&',/!001--.,))'&&&" + + +
?¦´¤œ©¯´´µ³³²°°°¦–Š…‹”›¡¤£¢¡ž›™•“‰ƒ€|zyyz{zxvvtuuuuxz|~€‚‡¥©¬®±³µ¶·¸¹¼¼Á‘5:497j¾µ²°¬¦¢Ÿš””„9=š˜™¢ˆ ! "#"7˜¬¢¡ Ÿžžœ›˜˜”? A £¦«³µºÁÅÈËÌÌÊËÉÊÆÁ¾¶« œŽ~volosuvz}ƒ„……‡Œ•œ¢©¯²·º¹´§ˆ|‹ž¬²³³³¯¢„6
+ + + + + + +'++)(('&&&&%%%&%&&&&%&(1#/.,,++)%&&%$#
+ + + + + + +
b²´£©²µ´´³³±±±¯©œ…‡—ž¦¥¤¢ œ›™–“‘І…ƒ||~~}zyxxx{}€ƒ‰‰•Ÿ¤¦¨©«®°´´¶·À‚+4361w¿²¬¨¥œ››˜e)›•™ž¤J$""#7‡§Ÿšš™——˜˜›¡Œ@# ¥£©°·ºÁÇËÌÌËËÊËÉÆÂ»¶®¡šƒwninsvwyy|€ƒ„„…ˆ‹‘𠦮±´¸º¸±œ€ ²´³³®š€Y
+
!*-+)(((''&%%&&&'%$%%$$&+/$****&%&$$$"! + + + + + +
"‡·¸£ ¨®²´´²´³²²²°©Ÿ‘ˆ…Œ•›¡¤¤¤£ žœ›—•”‘‹‰„ƒ‚ƒ„„ƒ€}|‚†Œ˜£¥›“™šœžŸ ¡¥¨ª®±¯¹q'0/0,n±ª¥¡ž˜b9X˜“”—šžž¢(!#!! *f‘œœ™™˜šœ’h1 !Tª¤ª°·»¿ÆÌÎÍËÊËÊÉÅÀ¼µ®¥—Žƒxoimqtvxy{~„„…ˆŠ‹• ¦«°´¶¸¸¶«“{“£®²µµ²«™€y!
+ + + + + + +
%)++)(((''&&%%&&&&%%%$%%%(3$)(%$##$##" + + + + + + +
5¡¸¹¢ž§¯³´µ³´´²²²²¢”‰ƒŠ–šž¤¦¦¤£¢ Ÿœš˜–•–•“ŽŒ‹Š‹ˆ‡‡†ˆŠ‘™¥µ¶«˜’‘”——› ¢¥§§°]!&'&%,@DAC<90&G‘’•™›œ§t 2Sq}|{qV5 =ž¨ª²¸»ÀÄÉÍÍÌÊÊÊÇÄÁº´¯¤›‘†{qklqstvy{|~ƒ…ˆˆŠ’›¥ª®²´¶¸·´¦†zˆ—¦°µµ´±©–ƒˆI +
+ + + + + + + + +%*++)(((&&%%&%%&&'%$$$%$$%)/('#$&##$" + + + + + + + +
_¶»º¡©®²³´´´´´³±²¯¥˜‹„†˜ž¤§¨§¦¦££¡ž›ššš››˜–””“‘‘‘–¬¶µµ®©•ˆ„‰”’–™œžž§NNˆ‰ŒŽ•˜œ¦Z#%$! :—©¨®¶¼ÁÅÈËÍÌËÉÈÆÄÀ»µ«¥œ‘†~skkpsrtvy|~„‡‰‹‘™£©®±³µ¸º¸¯žœª°µ¶µ°§•‡o
+ + + + + + + )*,+)((('&'&&%%%%%%$$#%%%&'(('%%%##! + + + + +
!‡º¿º¡œ©°³³´´´´´´²¯§›‡„‰’›¡¥¨¨¦¦¦¤££¡Ÿ ¡¡£¢¢¡Ÿžœ›ššœ ®µ´¯©¢–…|{|~†‰Š’““–C'b‡†ŠŒŠ‹Œ–˜›¢WBœ®¨¯´º¿ÅÉËËËËÊÈÇÅ¿º´«¤’„}ukmprrsvyz|~ƒ‡‹Œ’˜ ¨±³´·¹»¸—}‚‘ ¬±µ·¶²§”†-
+ + + + + +")++**)))('&'&%%&&&%$%$%%%&&%&$$$""" + + + + +
+
9¡»Á¹¤œ§®°±³³³´´³³³±«Ÿ‘‰…†Œ”𠤦¦§¦£¤¤¤¤¥§¦©§©ªª¨§¦¥¤£¥¬²¯¦”Žƒzvrsuwy}ƒ……†…‰6'Rˆ„†‰‡‡‰Š’–˜˜™—h" W ¬ª°´ºÂÇËÍËÉÉÈÇÅÃÁº´© šŒztlmorrsuwz|…‰“•œ£ª°´µ¶¸»º¶¦‰{Š—¡«±´·´°¥’H
+ + + + + + + + !*+*+*))(((&&&$%''&%%%%$%%%&$%$#"! + + + + + + + +
X³½Ã·ªš©°±±²²³´³´´²£•އƒˆ—¡¥¦§¦¤¤¤¥¦¨©«®¯°°±±±±°«ª¬¥™†€{upnmnopquxz{zyzy=-Ce{‡†…‡‡‰‰‹‹Ž”™˜•”‘•C<®©ª³·¾ÃÈËÌÌÉÈÇÆÅÃÀº´®¥›Ž€zvllnqrssuxy~„‰Œ’–›¢©°´µ¶·¸¹·¯šŽ›¤«°³¶´¯¤’–•“n
+ + + + + + + + + +!$*+****))((&''%%%%%&&%%%%%%$%$$# + + + +
~À¿À¹•¦¬²²²²²³´´´µ²¯¦™‘‰‚ˆ“˜¢¦¦¦¥¦¦¦¦ª«¯±³´¶¶·¸·¶´¯¬¥’ƒ~zxqnnlkkkloprssssspmfca\[X[[[Y]bju|†Š‰†„‡‰‹Š‹’’’’“•’’“’”’zJ))As¢ª³¸¾ÄÈËÌÌÊÈÇÆÅÿ¼µ±¦œ‘‚ztnknprssuuxz~ƒˆŒ“—›¡©®³¶··¸¸º·«“~…‘ž¨²µ·µ°¤š™–Ž8
+ + + + + !"',+******)((('('&&&('&&&%%$%&#$# + + +
+
.ŸÅÿ¸¯”¤¬²´´³´³³³³µ´²«œ’Š€ƒ‰‹’™ž¤¦§¨§§¦§ª°±³µ·¹º»»¹·¯©›‹€zwuqomjjiikmooopqpppmmklnnmpsx„†‹‘‹ŠŠ‰‰‹’“‘“”“““’‘‘““‘’•‡saOINW`v ¨§«¯³¸¿ÄÈÊËËÉÈÇÅÃþ·³«£˜‹ysokmpqrrtwvx{€†‹“–𠦳¶·¸·¸¹º³¥Š{†“¢©¬³·¸µ¯¤ •˜Y
+
+ + + + + +!(,+***+,+*)()(')(&'&&&%&&$#&&"# + + + + +
QºÄƽ·²—¢¬±´¶µµµ³²³µ´³¡—‚‚…†Š•œ¡¥¦¨§§¦¦¨ª¯°±²·ºº¼½½·¬©Ÿ’‹wvsonmkijkmnnnopoooonllmllmotz}…‹ŒŽŒ“–•’““”•““•“‘‘‘Ž‘—˜—˜œ¡¢££¦«®´»ÁÆÉËÌÊÈÇÇÆÄ¿½·°¬ ”ˆ~yrjlmnoqprvwxz„‰Ž’–šŸ¦²¶¹¹¸¸¸¹¹°˜~~‹š¦ª®´··´¯¡’£¦—˜v
+ + + + + "(++++**+)))))('(('&%&%%%$&''& + + + + +
{ÂÃŽ»¶– «°´µ¶µ´´³³´¶´¯¤š‘†ƒ†…‡Ž•› £¥¦§¦¥¦§¬¯±µ´¾ÄÁ¿À·«±¬©¢—Šzwtrpmnmnoopqpppooppomnopptw{‚†‹‹‹ŽŽŽ‘”––•–––––˜™—“‘‘‘‘““”••˜šœ¡¦©²¶½ÂÇÉÊÊÈÇÆÆÅľ¸±¨¢—Š€zrkonqromoqsy{~‚‰Ž”–›ž¢©±¶¹¹¸¹¹ºº¶§}‚‘Ÿ©¬°µ·¶³®œ¦©”Œ4
+ + + + + + +!(*+,,+*+*+)('''')(&&'%%%%&'&% + + + +
- ÂÄÅ»½¸“ž©²µ¶´´µ´´µµ´°¨œ“ˆ‚„„‡‹‘—¢¢£¤¤¥£¥¨¬µšPmš»Å¶¶··³¯¥œ’Šƒzvvsrrssusstrrstqqrtuuvuw|„‡‰ŠŽ’•––—˜™˜™˜™™™™–’Ž‘’”••—™›Ÿ¡¥«°µ¸¿ÆÇÉÊÉÈÆÆÅÄľ¹³ªž•Œz{kOB9ATjrqru{~ƒ‰Ž“–™Ÿ¢§´·¹¹¸¹»»º² …}‡•£«®±µ·¶²¯™“¨¬ ––V
+ + + + + + + +")++,,+***+)((('()('(''%&&&&&% + + + + +
M·ÂÆÆ¹¿¹–¨®²³´µ¶¶µµµµ´²« —Š‚‚„…„‹“šŸ¢£¤¤¤¤¤¦©´Z")0P¬Æµ¸º½»¸·´¥š•‹†}||||zzxxxxvuuvzyyyvvy~ƒ…ˆ‹”—š››œ››››š››—–‘ŽŽ‘””•—šœŸ£¦¬°µº¿ÄÉÊÊÈÈÆÅÅÅÄÁ¼¸±« ˜Œ‚z{_*$Inwx~‚ˆ’–™ž¢¦«²¶¹º¹¹¹º»·š‰š¤ª±¶··³¬–•¨¬£˜˜t
+ + + + + + + + + +!)++*+,***)((())(((&&&''''%%'% + +
rÅÄÉĹ»˜š¦®²´µµµµ´´´µ´²¬£šƒƒ„ƒ„‡Ž—›Ÿ¢¤£¥¤¢«”*(+)8¨È¶·º»¼¾½¾½»¸¯ª£Ÿ˜“‘ŽŒŠŠ‡…„„„‚€|yxyy|}‚…‰Œ’—›ŸžŸ¡¢¡££žœ›™–•”“’’•–•˜›ž¡¦«®´¹¾ÂÇÉËËÉÇÆÅÄÃÿ»µ®¦›“Š~wzV,f€†’—™ž£¥ª°µ»¼º¹¹ºº»µ¦}†’ž¦«°´¶¸¹³¬——ª®©›˜:
+ +"(++++,*)***+*))())'&&'(''&''& + + + + + + +
#ÈÅ˺Á¹›š¦±µµ´´³³³³´·¶¯¦›‘…‚‚„ˆ‘—˜™›ž¡¢£¡ª](*(=µÆ³¬¶¹»½À¿»Â³Ÿ§±¶·´©¨¥¢Ÿœ™—–•’‘ŽŠ…|zx{„ˆ“˜¡¥¦¥¥£¤¤£ žœš˜–••“”–™ššœž¡§«±µ¹¾ÃÆÊËÊÊÈÆÆÄÅÄÁ½¶°§œ‘Œ†{vwY!%k„Š‘•˜œ¢¦¨®´¸¼½»ºº»»º±ž‡‡— ©°´¶··³ª“𫝫Ÿœ–W
+ $)*********+*))''))((''(&%&''(
+ + + + + + +
:ÇÈȽ¾Ãº˜¦±³´¶µ³³³³´··³©”ˆ‚}|Œ”––˜šœŸ ¢¡8"&'L»Á³¬´¸½ÀÁÀŸW?GRap†¯¹µ±®«©¥£¢Ÿœ™–“‡}|}~‚‰–›ž¢¥¦§§¥££££ žœ›š™™˜™šžžžž¡¥ª®³¸½ÂÇÊËËÊÈÇÆÅÄÅÄÁ¿½µ¦—‰ƒ{uwa'8ˆ•™œ ¤§¬±·½¿¾»º»¼»·«’€Š˜£«®²µ·¸¶±¦«±¢ž›s
+ + + + + + +$)+++++++))*)*,((((((('&''%'(' + + + + + + + + + +
S¿ÄÊɼÂú–£¬¯²´µ¶´²²´¶¸¸´¬¡–‹ƒ|zx{ƒ“”—šš›š ‰ %%S½º°ª°¶½ÁÁǽ]088485H²Â¼º¸·´¯ª©¤¡Ÿœ˜“Љˆƒ†Œ”›Ÿ£¥¥¦¦§¤¢¢¢¢ ŸŸžœœŸŸ¡ ¡¥¦§ª¯³¹¾ÀÅÈÉËÊÉÇÆÅÅÄÃÅ¿¸whed_x}ttm,E’“–›Ÿ¡¦«¯µº¿À½º¹»¼º´¢‡„œ¥¬±³¶¸·µ°£Ÿ±¯£ œˆ3
+ + + + + + +%+*)**)*)'(())*'''('&&&&''&''% + + + + + + + +
sÆÇÌ˽Âļž’¢¬¯²´µ¶¶´²³µ·¸·°¤™…€~zxx}ˆ“•——– m Z¼²©¦«±¹¼ÃÂ`0234367\ÃÄÂÂÁ¿¾»¸´°ª¥£¡ ›’•œš˜Ÿ¤¦¨«ª©¨©©§¨¦¥£¤¦§¥§¥§©¨ª®±·»¿ÄÉÌÐÓÒÐÐÍÈÅÄÃÃþ¼«:!kxrr8+Pae\?}—•™¡¥ª¯µ¹½¿¾½ºº¼¼¹®—…‡•Ÿ§®±µ···µ¯Ÿ¢®²°¦ŸŸO
+ + + + +'+)))***('()))((())'&&%'''(''& + + +
+ + +
(–ÊÊÌȾľ¥“¢¯²´µ¶¶µµ´¶¶¸·²§š†€€{vuu{†‹‘’”œVR³¨¡ ¤«²·¿i35465572zÉÄÆÅÅÄÄÿº¸·³®ª±{9BGQs¬©¯¯°±°°¯®®¯¬®¯±²±³´´¶¸»ÁÆÇÁº´©œˆ…†™·ÊÇÂÄÂÁ·´Ÿ/2tpyS?mtuv|‚f(Vš–›Ÿ¡¥¨³·»À¿¼¹»¼»³¦ƒ‚Œ—¡ª¯²µ·¹·´«š’¤³±§Ÿ –l
+ + + + + + + + +
'+**)+*))***)))**)('(('&'&)('% +
+ + +
<²ÈËÌÅÂĽ¤“ ¬±²´¶µ¶µ´´µ¶¸·´«“„€|xttu}ƒ†ŽŒŽ‘A6žŸ™šž¦¬¸|,311566:;–ÅÂÅÆÅÅÅÅÅÅÄÃÀ¾¹¸ªE-,-+Bµµ··¸¸¸¸¸¸··¶¶¶µµ¶º»¼½¼¿Çȼ fYNG@:62118M¹Å¿½º¯ª#Nqqc"Grosw|…j<œœ £¥¨¬¯³·¼ÁÁ¾¼ºº»¹¬ŸŒ‚„›¥«±³¶·¸·²©˜“¥®²±©¡¡“y.
+ + + + +!(*++*))+)*+))))))(()*)('(''&%'
+ + + + +
+
\ÁÈÌËÃÂÅÁ»§“ª±´µµ¶¶µµ¶¶·¸¸´¬¡˜‹‚€~zvrruz‚‹Œ‹6{š’”™ž©Œ.)*,./37:F¯ÁÁÃÃÃÄÄÅÆÇÈÈÆÄ¿Ãk43.,,2šÁ¼½¾¿ÀÀÀÀÀÀÁ¿¾¿¿¿ÀÁÂÂÅËÄŸsU<6533332//.0/25R¤À¶²¨§t!gln1?uoty|ƒ‰Š’‘Ÿ ¤§¨©°±¶º½ÀÀ½»º»º´¦“‡„ˆ’¡ª¯²´µ¶¸¶±¦”—§¯²²¬¡¤˜„Q
+ + +
!*+***+**)**))*)))'(())('''&'&( + + +
+ + + +
ƒÅÇÌÊÃÄÇý¬”™¦¯³¶···¶¶···¸º¸°¤™‡ƒ€{xuqpsy}‚‡‰‰/F‰”™Ÿ9'&(,,/56WÀÀÁÃÃÂÂÃÆÇÇÇÆÆÅÏŠ8630163xÇÃÅÆÄÄÆÆÆÆÆÅÇÈÇÇÇÉÈÉÏÉd=54642-,'')),020243A•µ§ œXBpmL.tuv|†‹Ž’˜¡¦¦ª«««¬¯³¸½ÀÀ¿½ºº»¸¯œŠ„†Žš¥¬°²´µ¶·µ°£’œ§®´´°¢¥ Žj
+ + + +!$*.+,+,,,*)****)))**)**(()('('%' + + + +
+
, ÅÈÍËÃÇÈľ¬”˜£±µ·¸·······º¹´¨œ“‹ˆ…|ytpnqvy~€‚+!!r‡†Ž˜e !$%).45nÁ½¾ÁÃÃÂÃÇÇÆÆÅÃ΢?5210148S¼ÈÈÉÈÉÉÈÇÈÉÇÈÉÈÊÊÊÌÑf=6651++>TgtthR4-42.00.‡®‘‡:"fmi'axw~…‹•–œ ¤§©ª«ª««¬°µ¼ÀÂÁ¿¼»»¼¶©”ˆ†‰’Ÿ©®°²´´µ¸¶°£“¨±´´¯¥¦¥“y-
+ + + + + + + + +
33-,,,++*+*+*)*)))++**)(()&((&' + + +
+ + +
=³ÈÊÍÉÅÉÈĽ®—™¡©°´·¸¸¸·¶··¸¹¹¶ž”Œ‰‡†‚|wsonpruvx+,F=†ƒ‚) %*/3À»¼ÀÁÃÂÃÆÆÆÆĄ̈F4/..-.129ŸÌÈÊÊÊËÊÉÊÊÊÉÉÉÊÉÎÇ‚F523/*<d•°½ÃÁÁÁ½¥d,*,+&q¯¢—Šn"AnoM8~z‰”—œ ¤¦§©ª¨§¨ª«®´¹½ÁÂÁ½»»¼¹°¡††‹—¤¬¯¯±³³¸¹¶°Ÿ’ž©²´´±§¥¤Ÿ‰G
+ + + ++81,,++**+))*++)()***)((('&(('(
!% + + + + + +
RÀÉÉÍÇÃÉÈľ¯›–Ÿ§®³µ·¹¸··¶·¸¸¸¶¯Ÿ”Ž‹‰ˆ†€{uponmoor;%jlƒ]!%+3”¾»¼½¾ÀÂÃÅÅÄÃ˳K50/-++++,+rÍÉËËËËÊÊËÌÌÌËÉÈи_5824,/i¯ÇÈÄÁÀ¿½»»¿¿ƒ-(&k±¤™‰S]jn0]„„‹‘—š £¥¦§¨©©§¦§¨¬±µ¼¿ÁÁ¿¼»½¼¸«˜‹†ˆœ¨¯°°±´¶¸¸´®›’¡«²³µ²ª¤§¢”g
+!"+38--,++++**+-+()()*++)((''((((
!$'),/ + + +
rÇÉÌËÄÆÈǽ°œ–ž¦²µ·¹¸··¶···¸µ¯¤™“ŠŒŠ„~zsqommmqU oB?ˆ01%1¡¾¼½¿¿ÀÂÄÆÄÁĸS/110*))))+*H¼ÍËËËËÊËËÌÌËÊÈѱM4231+K¡ÉÆÄÃÄÃÂÁ¿¼¸¸¸¾…1d®¤œŽ‚}4.nmg x‹—› ¤¦¦¦¦§¦¦¤¥§©®±¸¼ÀÂÁ½»¼¾»´¤‘‰‡‹”¢°°±³¶¸¹·³«—”£¬±³µ´¬¥ª¦žƒ<
+ + + + + + + #*.69-,,,+*)*)*+**)))*,+())))()'"&,-/005&
+
*—ÈËÎÌÅÈÉÇÁº°œ”›¦±´·¹¹¸···¸¸¹¸±©œ“މ~zvsmnnmj%XpolD[3¢¸¶¾ÀÀÀÀÂÃÃÅÀ_3522-9I'*+./ŽÐÈÉÉËËÊÊÌËÊÇͳO3311)`¾ÌÄÄÅÄÄÂÀ¾¼¸µ´±¯¯”¨¥Ÿ–‹~m%Dwu[0‰Œ‘™ ¢¤¥¦¦¦¥£¤¤¦¨°³¸¾ÁÃÀ½»½¾¹¬™Š‰‰˜¥±°²´¶·¹·°§–—¦±´µ´¦©¨£•^
+ +
!! #%+,-:7,-,**)))*,*)*))*))***))((' $)+-0101* + + + + +
?²ÆÍÐÍÅÊÉÅ¿¹¯ž”—£«¯²¶¸¹¹·¸¸¹¹¸¸´« •‘“”‘Žˆ‚€|wuqomp::yO8=xc5¢¬®´¹»»»½¿ÀÊ|4:6479—σ(,+-.XÆÇÈÉÊÉÉÉËÊÈÈÆ]3400(eÄÆÁÂÂÂÁ¿¿¼¹·´²°¯®¬¬¥Ÿš’‡‚eS…ƒT;’˜Ÿ¡¢¤¤¤£¤¢¢¢£¥©¬±µ»ÁÃþ»¼½¼²¤‘‹‹“ž§°°±³µ¶¸¸µ¯¥•™§¯²µ¶´¯¨©¬¥•t, + +! ").-,181,+*)**+++)*+*(()(),)(*('( ""$'*+..1200. + +
^¿ÅÍÐËÆÌÉþ¸± •–¡ª°´µ·¹¹¸¸¸¹ºº¸µ¬£™‘’—˜”‰ƒ€}zwrnpO%kv,CŒi2𤧲´µ¸¸¶Àš866675|ÇÄ7../17¢ËÇÉÈÈÈÇÉÈÅË‹4402/I»ÂÀÁ¿»¹¸¶¶´³°¯®««©§¦¢›–Œ‰eX“Y:˜˜›Ÿ ¢£¤£¡¡ ŸŸ¡¤¥©®²·½ÁÃÁ¼»¾¾¹¬šŒ‹‹Œ˜£«°°²³¶¸ºº´¯¡’›¦®³µµ´±ª¨°ªš…P
+ + + + !!&..,+49/++**++*+)***)()((+*('(()!%()))+.///13331
ÆÈÎÐÊÈËÇþº´¤—•Ÿª°µ··¸¸¸¸¸¹º¼»¸¯§•”š™“ŽŠ…ƒ€}xstaWyalˆm.”ž¡¦¬®±²³¹´I*0-/.Wº½¼¿Y-2013pÍÇÇÇÇÆÅÆÆÈÂ[21041ü¾½»¶µ´²²°®¬«¨§§¤¡žš”“‹Œj R™“g0”Ÿ ¢¢¢£¤¢ ŸŸž ¢¥«°µº¾ÁÁ¾¼¼¾¼²£”‹‹Š›¦®²°²³¶¸º¸µœ‘›§¯´µµ·³©±ŸŠt& +
+ +##$#!#+,./-.65--+*+*)****)**)*)))))))* '''&(0./20021/445558
*žÈÊÎÎÉÉËÆÂ½»¶¥—“𥮴¸¸¸¸¸··¹¼¼»¸³ª ™‘—š˜”Œ†‚zvn$Auu=6z|p/“¡£¦ª¬ª·u%)*)+2™º¶·¾€*1//1I¹ÈÅÆÅÃÃÂÂÆ¬>3./3Aº¸··¶³³²°®¬«©§¦¦¥¢ž›—“‘‹Œl H˜”w!!ƒ¡ ¢£¤¢¢ Ÿ Ÿ¢¦®´¹½ÁÃÀ¼»½½¸¬šŒŠ•ž¨¯³²²µ·¹º·³ª–©°³µ¶·¶¯©²¯£F
+
##$##%,,,/0.164.-,+********++*)))**(''((+.0/4956:758;89=@9:;7
B±ÇÎÏÍÉÊÊÆÂ½»¹§—“˜£«±¶¸¸¹¸·¸¹»½¼ºµ®£›’‘˜š˜”‹†„‚€{x(/rrkW}{y%/ššœ ££¤¨œ3$&%'%c·±³µ¹¢1,.,14ƾÁÁÁÁÀ¿Å21,+0T·´µ´²±°°±¯®¬°®®®¬¨¤žš“‘‡†‹‘z$<”“%d¥¢¤¤¤£¡ ŸžŸ” ®°·»½Á¾¼¾½»³¤”ŽŒ‹Ž–£¬±±±²µ¹º»¹²¦‘’ ª²µ¶¶··µ«®¯¥‘…f
+ + + +$##$$&-/.11-/172-*-+*****,*)()*))***''(),1-.11011345578<<==<5
V½ÇÎÎËÈÊÊÆÁ¾¼¸©š“™¢©®²¶¸º»º¸¸¹¼½¼¶°©Ÿ•Ž”›š—•’‹†…ƒ~}/'suyM#r}|}**„”•“”˜™›¤b 1›¯¬®®³S&*(+*_¿¹ºº¼¾¾ºÁ}**)(*]µ¯°®®¬¬«ª¨©s`b_[USKC<81`‘’‚)0Œ•Œ,:¢§¥¤¢ œœœœ JV¸¶¼¿ÂÀ¼½¾½º¬™Œ‹Œ“ž¨°±±±³¶¹ºº¶¯ •£´µµ¶¸¹¸°©™…z,
+ +%$$%%)-0/00-,-26.+,,**+**)**(**)))))'(,+-../00//3355565799:95 +
vÃÇÎÎËÉÌÉÄÀ¾½º¬”–¡©®²³·º¼¼ººº»¼¼¸°§¢›“‘–››™–’†„„…3"mzvu)E€…‰0*…Ž‘’‹,X¢¢¦§¨§®s$#'&5¡¸±±³¶¸¸¼r%%%#$T®¨¨§¦¦¤¤¢¢¢¡C!Q’‹/(„““@ f«¡¡¡ž››››˜¤w'$R«¿ºÁÀ½»¾¾¼³¤“ŽŽ˜£«±²²±³¶¸»¹³œ‘—£´µ¶¸¹¹¸°«¯¡ˆ€Q + + +&$$%&*///0/-,,-45,++++++++**)))()*)*'&3/000.10038766777596987!
&˜ÃÉÏÏËËËÇÃÁ¾¼º¯ –•ž¦¬°´¶¸»¼º¹º¼¼¼º´«¢–’’˜š›™—•ކ‡‡2!o~{}\f‚ŠŽ3+‡ŽŒ‹ŒŒ‘\$|”—›žŸžž’* #j±§ªª«ª«±m""C¥ žžœšš˜™œ›> T‹Œ’4!}‘•_ '~ª Ÿ››š™— ”1"'$D¾¿¾º¼À¾¹¬˜ŽŒ“§¯±±±²´·¸¹·±¨—˜¥³·¶¸¸¸¸°¨¯±©’€r
+ +"&$$&(,//0/.--,-.72++++)*(*))))))))**((6324301128;667:88:=8:9:( +
=¯ÄËÐÐÌËÊÆÃÀ¾½»²£˜”œ£ª¯´¶¸¹º¹¸¸»½¼¼º°¤›—’“•˜šœš˜•‹‹8"sƒ€~32„‡Œ‘1*„‹ˆ†…ˆ…25‚”˜œŸ¡šžQ2™¢¢¢ ¡¡¤o/Ž“’‘Ž‘–˜“7_ŒŒ’: y’•„'#.~§žšš™– œ='&(&~û¸¹¿Á½¶¥“Ž˜¢«±²±±³µ·¹¹µ¯¥–‘𥮳¶·¶¹¹¸´«°³¬œ€„5
%''&&),...-----,,091,,.,*)**())))()++**<8798549<9:6::<<=AB?@A@7 +
f¹ÄÍÏÎÌÍËÄÂÀ¾¾¼´¥™•›¡§®²¶¸¹¹¹¹¸»¾¾¾»³©—“‘‘’•˜š›š—•’’:!x‹‡ƒŠa^‘“–‘0(€Ž‰‡ˆ‡Žd(6@JOT[`cIh¡—–—™˜›z]‡‚„…‡ˆ‘”–’1_ŽŒ7 v–•žK! )qœ¡›ž¡‹>&%&%9®¼µ¶»Á¾¸š”𦮲²²²´¶¹º¹¶£’‘œ§®²µ¶·¸ºº¶¯³°£‹ˆ[
((*))+,///.-,-.-,,27.,,-,++*)))))()**)*<<=><?=BD@@>AB@CEEFEHHGO +
4™ºÄÎÏÍÌÍÊÆÂÀ¾¿¾·¦›“𢍮²³¶¹¹¸¸¸º¼¾¾¼¸®£™–““–˜˜™—–’4&}Š‹‡>‡™——˜‰&#yŒ‡†‡Š‹6-‹‘І}.,{‚ƒ‰”••H.--/-$fŒ‰†3"{“—Ÿ+ FrxZ&#"$$²²¸ÀÁ½°ŽŽ‘— ª°³³²²´¸»½»·¬ Ž’žª°³´´·¸º¹·®®²±©•ˆy
()*+,......--,-..-.22,,,+-+****)*)()(();<<?BDABEGDEIJFFGIKIILMT4
e±ºÊÏÏÍÌÎÊÅÂÀÀÀ¿¹ªž‘—¢¨®°³¶·¸¸¸¹º»¼¿½¹²¨œ—“‘‘‘’’”•––˜’.)‘“‹–˜š›™!q‡„„„‰lU†„ƒ~}€FC}}‚„‹ŽŽŽŽ‘’“•sm‡„0$~–™œ§t"$#!"$%_Àµ¯´¼Á¼µ§“ŒŒŽ“›¥²´³²³¶¹¼¾»µª—Œ” «°³´´·ºº¹¹±ª¯²¯†ŠG
')))(*.--.,-/,-013.063,+*,-*)*+*****)**==>DFEEFKLLJMNNOKNLMILR]C
.œ³½ÎÐÎÌÍÌÈÄÂÀÀÀÀ»®¡’“Ÿ§°µ¸ºº¹¸¹º»¼¾½ºµ¢™•“‘‘’“““”•••tH'-†“˜›šš™™~k‚‚ƒ‡?#xƒ€{{zkJ‚‚‡ŠŠ‰Š’•™˜”–tnЇ+&ƒ—› Ÿ¡b ""!!"#T¸¸®±»Á¿¸«›Œ—Ÿ¨±µ´³³µ¸»½¾¸°¤•Ž—¡«¯±´´¶¹¹ºº³ªª°±¤‹‹k +
*()((&).0/.-././12:82270,++++++++*))*+**BBFIJJKKNOQLNOOPNOMLOQPSD
\¹°ÁÍÏÌËÎÌÆÃÁÁÁÁÁ½±¤•’𥬲¶¹»»¹ºº»¼¼¼»ºµ°§ž˜”‘“––—•’“••œˆ}—“‘”™œœ›˜–‘{"`„€€„r!'M‚}zx|OG‡ˆŠ‹Œ“•–•”\!u‰†t#+…–œŸšŸV!""$#f½¹¯®µ¿Â¼²¤”ŽŽ”œ¥°³¶´³³µ¹¼¾¾¸¡”™£ª¯±±³µ¸º»¼·¬ª¯²¬”†ƒ.
#*((('(*.0//..////0<N6-47-*+**+**++)(*,*(ILJIKLJJLOSRQOOMMNOLONLIH
#–¿´ÅÌÍÊÌÏËÇÂÁÂÁÂÃÁ´¦˜“˜¡©°µ¹ºº»¼»»¼½¾½»·²«¢š••–˜———”‘’•–— ›˜–˜šœŸœ˜•y"W‡€~„D uƒuncYNJCA=81-*h{z|{xuy76rŽŽ’“–˜š˜š™s#$|‹‰r,ˆ—™œž Ÿ] !"$";‹Ã¾²¯³½ÁÁ¸ªœ“˜¡³´¶´³´·º¾À½µª‘š¤ª°°±´µ¸º¼»¹®ª®²®‡‹O
!'''((**-../.-//.,/1KK-.78**+)**)*+*))***LOPPQRQONOPSSRSPLNONJKNMM1 +
YÀ¸¶ÆËÉÊÎÏËÆÂÁÁÁÃÅÆº«š“•œ¦¬²¶¸º»¼¼»»»½¾½º´¬¦Ÿ˜•–—™š™—”‘’•—˜™œŸŸ™–—˜›™–’|%Jƒ~€v#D‡ƒ‚…ˆŒŠ‰ˆˆ‡†ƒ~|c7{ywxvtvv/ R„˜ž ¢£žŠW,ˆŒŒk*‡œœŸ œœo8!*I€²Ä¼·¯²»ÁÁ¼²¢’ŽŽ‘•›§¯´´µ´´³·¼À¿½²¥–’¤ª¯±´µ¶¸º»»¹±«±³©‡q
&''(''(+.../..-..-/15B7,/63,+*)()**,*(*+*PSSPSXTPSMPPRRSPNNLKMOQROA
šÆ´ºÊÍÉËÏÎËÆÂÀÂÃÄÆÇ½¬ž”•𢩝³¸º»»¼»»»»½¼»·°¨¢œ—––˜›››–“’“””’“˜žœ—–••”’'A}ƒWn‡ƒ…†„†‰ˆ‹‹‡…‚~{€AW}xxywy€x1%=[nxvfG*WŒ‰e)‡Ÿ £¢ œ™–š“v`Ycw—³¼¼»¹²²·À¿¶©—Ž‘”˜¡¬³³²´µ´µ¹¿¿¿»¯Ÿ’“ž¦«¯³´·¹º¼»»»µ©«¯²¯—‡‡='(''&((*.../00-,-./1034-,05/**+*,,++*)***WWWY]^[YXTXSZ]ZURRSQPRWTMK
VÅó½ÌÍÇËÑÍÇÂÁÁÃÄÅÇÉÀ° “”™ §¬±·º»»»»»¼¼¼¼»¸³¬£˜––™œžžœ—““’‘’’˜™™—”‘Žˆ†„?#7}|/?‡ƒ‡‰‰ˆ‡‰Ž‹Š‡ƒ}||t ,y}{}ƒƒ‰‚=E’‰ˆŒh&„žŸ Ÿœš˜——Ÿ¢¤¨ª±µ»»ºµ±·¾ÂÁ¹ž”‘’–œ¦¯´´´´´µ¶»ÀÀ¾¸©›•¡¦¬±³µ·º½½¼¼¼¹®®°±¯ŸŠˆd&))''()(,/.-010/.-/110/1/,-26,)(,..-,+***)UUUVXZ[ZXYYTY`^ZWY[YR\[YVZ$ +
—Ì¿³¾ÍÊÅÎÑÌÄÂÁÃÄÇÈÊËĵ¤•“–¥¬³¹º»¼¼»»¼½¾¿Àºµ¯¥ ›—–™œžŸ¡—“‘“•——•ƒ€~{vpmfj€‡B-$g‡‰Š‰ŠŠŠŒŒ‡„~{zULƒ€†‹ŠŒ““T:’ŽŠ‹i zŸš˜˜–—šœž¡¥¨¬¶º½¼¹µ´¼Â¼±¢–“‘”š¡«³µµµ´³´¹¾¿¿¼³¤•‹˜¢©®±³¶¹»»»¼¾½º±®°±°ª’‡|)*+*)**))....-//...020/-.//,,43-++,,,,+***)NOQQORRRSPQNMSPNQPQSSWVWVU5 +
N¿È¾²ÁËÇÆÐÐËÄÂÂÄÆÊÊÌÏÆ¹§—’”›¡¨°·¹¼½¼»»»¼¿ÁÀ¿¹²¨¢Ÿ›˜——›ž¡ œ—”“’’””’‘““Žˆƒ~~ƒ‹”™ž–‹€qjŠŒŽŽŽŽŠ‰†ƒ€~~4#r‡Š’“””—œy0?ŒŽ‰‹‰‹`({ š˜˜—–˜™œ¡¥¨®µ¼ÁÀ¼¸µ¹ÀÁ½³©›“’’•œ¦°¶¶´´´³µ»ÀÁ¿º¡’Œ›£¬±²µµ·»»»¼¾½º²¯°±±¯Ÿ‹†I ++***))*./--...//.2510.../-+-64.,(*,,++*)(PRWVPOQTXTTRQQLLQOOOSVSTTSK
’ÊÆ»´ÅÉÅÇÒÐÉÃÃÅÇËÌÌÎÐ̾«™“–™Ÿ¦¯³·º»»¼»º¼¾ÀÀÀ¼¸®¦¡š——˜œžŸœ™–”•”•”‘Љˆˆ‡††‰Ž”šœž£¥¥£¢ž—–•––”“”‘Œ‰‡„€‚x][\[Z]|‘“—˜šžž Ÿ¥š_&U’ˆ‰ˆ‡‰hH[vŸ žš–—–—˜šŸ£¦¬µ»ÂÃÀ¼¸¸ÀÂÀ¶¬ “‘‘’•™¡´·¶´´²´·½ÁÁ¼³¨šŽŒ“ž§®²´µ·¹º½¾½½¼»³¯°²³²¨‘‡i,0-,,+++-//.-.///0/254//...-+-/71.+*++++*+*TXZXVQRYb^[[ZYXV\YVUZ][XW[_
TÁÆÃ··ÈÈÃÉÑÏÇÄÆËÏÐÎÎÎÑÐÃ°š””—›¢«±µ¸»½½¼¼¼¾¿À¾¼º³ª¥ ˜—˜œž›š˜•”’“””’‹Š‰‰‹Ž’–˜›œŸ¡¤¦¦¦¥ œœ›™—˜–—–’ŒŒ‹‹‹ŠŠ“•“–šš—™œž¡£¦¦¨§¦§©’`2D{•Œ‡ˆˆ‰‹Œ‘Ÿ¤¤¦£ œ—”•–™ ¢§«²ºÁÅÅÀ»º¿ÂÁ»°¢“‘““—Ÿ©±¶¸¶´³³µ»À¿¸®£”– ©°´¶·¹º¼¾¾½½¾¾µ°±³µ³¯›…D62/-,--.00/..//...04511.-.--,+151-+*++*+--TUTTTQPPVZUUVVVWWUWY\ZZZ[[[)
"“ÍÈÁ´»ËÆÂËÑÌÇÇÉÎÒÒÏÍÏÓÒÆµ —”“˜ž¨®´¹»½»¼»¼¿¿À¾½»¸±«£™˜–™Ÿ ›˜——“‘’•”‘‘Ž‘““’“•™œœžš™›Ÿ£¦ª©¨¤¥¦¢Ÿžš”‘’’””•š›Ÿ££¥¨ª«®®¬««¯Ÿ‚hT>638Kd…™“‹‹‰ŠŒ‘”šŸ ›–••–˜› ¢¥¬µ»¿Åľ»¾Â½±£—‘’‘“”›¥¯µ¸·µ³³´·¼ÀÀ¾¶¬Ÿ‘Š™¡ª±´·¸¹»¼½¾¾¾¾¾µ°±³µµ³¤Š‰Y2/+-.++.00.//00/-./1420/...,+,/43.+****+-,SQRSRQPWWWRQQTVUSRTUWVSUTTW9 +
PÁÈÈ¿²¿ËÄÁËÒÌÉËÏÒ×ÒÏÍÎÑÔʸ¡—”“”˜¥²·»¼¼»»¼¿¿ÀÀ¿¾º´®§¢›˜—šœžŸž›™™˜–’’–˜œœŸ ŸŸž››ž £§ª°³¯¯©¦¢—““••““””—šœŸ£¥©¯°±²³´µµµ¶¶µ´³®®«¨¢œœ ¡•ŽŒ‹Ž‘‘“•˜˜™šš˜’’“”—𣍬³»ÁÄÄ¿¼½Âý·§™“’‘’‘”™¡¬´¸·¶¶µ´¶º¿ÂÀ¼²¦–Ž‹‘œ¤°µ·¹º¼½¾¾¾¾½¼·±±³µ·¶«‰q4.,--*).///0/.//../07600.-..,+-/44,)**+++*[Y]ZT[[^eaZ]YX[YW\XXXW[XYVSG”ÎÈȺ²ÃÈÂÂÌÒËËÏÓØØÒËÌÍÑÔË»¤—”•”•¡«±¶¹¼½½»½¾¿ÀÁÀ¾»¹³¬§¡ž›š™šœŸžžœš™™”Ž“—œ £¥§¦¥¦£¢¢££§§¤¤¦¨«¬¬§¢™‘‘““•—–™œž¢¦ª®²¶¸¸¸¹º¹º¼»¼¿¼¸·µ´°©§¤£ œ˜“Œ‹Œ‹Š‹‹Ž‘“””’ŽŽ’•˜œ¢¨®³ºÀÆÇÄÁ¾¿Âľµ«”’”’‘“–œ¥°·¸¶µµ´µ¹¾ÁÁ¾¸¯ Š‹•Ÿ§®´·¸¹º¼¾½½¼»»»¶°±³µ·¶²™Š‚E20,)++.0/./../../.0<C4/00.-,,,,05.++,++*)UVUSTWUV[[X\VXZ[]_`acac_b^YT
LÃÌËǶµÅÈÃÃËÔÐÏÕÙÚÖÏÊÊÍÒÕ;©˜””“–ž¨®´¹»¼¼¼¼½ÀÁÁÁÀ½º¶±«¤ ŸœœŸžš˜˜•”‘‘•£¥©ª««ª¨ª©ª¬¬«ªª©§¥¢ š—“‘ŽŽ”–˜š¡§¬³¸¼ÂÃÁÁÁÃÂÁÃÂÂÀ½»º¹¸¶²®«§¢—Œ‰ˆ‡†„†ˆŠ‹ŒŒ‹‰’•›¡¦¬²¹¿ÅÉÆÁ¿¿Â¿· •“””“’“˜ ©³¸¸¶´³³¶¼ÁÂÀ¼´©˜‹‡Œ˜¢¨°µ¸¸¹¹»½¼¼»º»¼¶°³´¶·¸³¤‹c10-**,.0/////....-.2<512/./-,+*-23,*,+*()MKPOPSRQNRONMQRTVTUUWWXZ[WTS%ÐËÌð¶ÆÆÃÄÏÔÔÕÖØÔÏËÇÈÍרÐÁ«™”•““›£´¸»¼½½½½ÀÀÁÁÁÀ½¹µ²ª¤¡ŸŸžŸ¡ ŸŸžœ˜—–•’‘’“—› ¤§ª«ª¨§¨ª©©ª§¥¤ ›–’ŒŽ“˜œ¢¤«°µ»¿ÂÅÈÈÇÆÅÆÄÃÂÂÁ¿¾¾½½»»¹¶³¯ª£•‰‡„„„……†ˆ†…†‰‰Š‹Ž”›¢¥¬²·¼ÂÅÆÃÀÀÄÄÀº°¢—‘“•’“”𧝶·¶´³²µºÀÃÂÀº±¤’‡…𥫱µ··¹º¼¼»º¹¹»¼´°³µ·¹º·¬–Žy80.+**///..0.---,,-.02351-/.,++-.53--+)*,MOPSOQRSQSSXQSSRSSTRPRSSTSSO>C¾ÍÍÌÀ¬¸ÉÆÃÄÌÔÖ×ÕÔÐËÈÇÉÌÖÚÒ“‘‘”ž¨°µº¼½¾¾¾¾¿ÀÁÁ¿¼¸µ¯¨¤¡ Ÿ ¡¡¡¡ ›š™™—••—˜™™šœŸ£§¨¥££¢¥¥¥¥¢Ÿ›—”ŽŒŽ•—¢¦¬°¸½¿ÂÄÅÆÅÇÇÆÅÄÂÂÂÀ¾½¼º»¼½¾»·´¯¨£œ’ˆ…„‚‚‚„„„††…‡ˆˆ‹‘—¢§«°¶¼ÁÅÇÄÀÁÄÆÂ¹±¥™“’•“‘’˜£®´·¶µ³²´¸¾ÂÅÿ¸Ÿ‘‡…‘§¯²´¶¸º¼½½º¹¹¹¼¼µ±³´·¸¹¸°š‹†M00.,,00//.-,-./-+,-/0372..--,*,-051-)***PRRWPPRSSTX[RPSRSQROLNPRQPPPNƒÐÌÌʹ¯¼ÉÆÂÃÌÔÖÖÕÒÍÇÅÆÇÍ×ÚÓÆ²š”’˜¢ª±¶»½¿¾¾¾¾ÁÁÁ¿½¹·³©¦£ ¡ ¡¡¡¢ žœœœœœžžž¡¢¢ ŸŸœ˜–“”••”’‘‘’•˜œ¡¦©®²·º¾ÁÂÃÄÄÄÄÃÂÀ¾½¾½»¹·¶µ´´¶·µ³¯ª¢™ˆ„ƒ€€ƒ„ƒ…†††ˆ‰“™¤¨«®´»ÁÅÇÆÃÁÅÇý²¦›””””‘“Ÿª´¶¶¶µ´µ¸½ÁÄÄÀ»µ¨™Š„‡” ¨¯³µ·º½¾½»º¹¹º¼¼´±³µ¸¹ºº²žŠg/31,,11//0/-,...-,,..132/.--+,,-,/3.+**)TXSTQQSTWVVXPNRTTQSSPNOPNOQNT0C»ÌÌËŵ±ÀÉÇÂÂËÓÔÕÕÐËÆÃÄÇÌ×ÛÕȶ–’”¥´¸º½½¾¿¾¿ÁÁÀ¿¾¼»·³®«§¤¤¢¢££¡ ¡ ŸŸ žž ¡¢£¢¢¡¡¢¢¢¡ ›˜–••––—˜™š ¡£¥ª¯³¶¸»¾ÀÂÃÃÃÃÁÁ¿¾¼º·¸¶³±¯®®®¬«¨¨¦¡œ–‡€€ƒ…„‚‚…„†‹‘•𤍫¯´ºÀÆÉÇÃÃÄÅÄ¿´§““””’‘—¥°µ¶¶µ´³·½ÁÃÿ¹°¡‘…ƒ‹™¤ª®´µ¹»¼½½»¹¸¸»½¼±°´µ¹º»ºµ¥‹Œ~3*1+,/.////.-,-/-----./01-,,--,,+.030*'-UXSRSRWXZVVXPOQSTRPQRQQOOQPPY@
}ËÉÌËŲ²ÆËÈÄÀÉÓÕÕÔÐÉÅÃÅÈËÖÜÕʺ¢—“Ž‹˜¡ª¯µ·»½¿¿À¿ÀÀÁÀÀ¾½º¶³¬«§¥¥¤£ žŸŸ Ÿ Ÿ¢£¤¦¦¥¤¥¤¤£¢¡Ÿ›šœœ››œ ¢£¦ªª¬°µº½ÁÁÂÂÂÃÂÂÁ¿¾½º·¶³²³°ªª«ª§¢ ›˜“‡„ƒ‚‚€~ƒ…ˆ’˜›ž¢¦ª¯´»ÁÅÉÈÄÂÆÇž¶©–’“”’• ´¶µ´´³µ»ÀÁÃÄÁ¿¸«›Š‚„‘Ÿ¨ª®³·º»¼¾¼º¸¸¸»½º±²¶¶¸ºº¹¶ªˆŽF"//-.00.-..,-./.-,+-/02/,+,*+,,./47.+,TSQQSVTRWSUUPPPQPONPQSSSTUSTYM.«ÊÊÌËÀ¯µÆÊÈÅÁÇÓÕÖÔÏÉÄÂÄÇÍÖÚÕ˼¤–”‘Ž‹Œ’¦¬´¸»¼¾¿¿¿ÁÀÀÁÁÀÁ½¹¶±¯±©¨§¦¥¢¡žžŸŸ ¢¡¢££¤¥¤¤¤¤£¤£¡¡ŸŸ¡ Ÿ¡¢¤§ª®±´·¼¿ÃÅÅÄÄÂÂÀÀ¿½º¸¶´±¬ª¨¥¤£¢£ œš˜Šˆ…ƒ‚ƒ€}}~‚†ˆ‹’–› £¥¨ª¯¶»ÂÇÉÉÅÂÅÇÄÀµ¨›—”“““Ž“žª²¶µ´³´¶º¿ÂÄÄÃÀ¼µ¥‘…„—£©«®³¸¼¾¾¾»º¹¶¸½½º¯²¶·¸¹¹¹¸®”†Ž_.,,-/.----,--.-,++,.00,*++*,,,,/42-+RPMRUSPQSTSRSPPONRSOPQRQRTQQQS)eÉÉËÌʼ·ÆËÈÄÁÅÑÕÖÔÍÇÃÃÄÇÍ×ÛÖÍ¿¨™•’ŒŒ˜¡¨°¶º½¾½¾¾ÀÀÁÁÁÁÁÁ¾º¶µ´±¬«ª©§¥£ ŸŸŸ ¢¡¡¢¢¢ ¡¢£¥¤¤¢ Ÿ ŸŸ ¢¢¥¥¨ª®±µ¹»ÀÃÄÅÆÆÅÄÃÄÂÁ¿½»·µ³±°®©¦¡žš“’‹„}|}„ƒ}~ƒ‡‘–𢤧©¬°µ»ÁÇÊÊÅÂÄÅü²¨œ”“””“’œ§°´¶´³²µ¹¾ÁÂÄÿ¼²žŒƒ‚Žœ¥«¬³¹½¿À¿»¹¸·¸½½¹¯µ··º¼¹º¹±žŠŠv$,-,../...,,,-..-*+-/.++**,-,+++.4/+SPQUTRVVSSTSTQQRQSQOQNQOQROOOQ=(œÇÇÊËÆ¹«ºÆÊÈÄÀÃÎÒÔÒÌÇÄÃÅÇËÔÚ×Ï«œ–•’Œ“›¤«²¶¹¼¾½¾ÀÀÁÁÀÂÂÁ¿»¹¸¶´´²¯¬¬ª¦¤¡ ¡¡¢¡ Ÿ¡¢££££¢¡ ¢£¡¢¡£¥©¬®²µ¸»¿ÂÄÅÇÇÈÆÅÅÄÃÂÀ½º·´²°®®«§¢›–‘Žˆ†~~……ƒ€„†‹‘•–™œ £¦¨©¬°³º¿ÆÈÉÅÂÃÅû±ªŸ–““”“ŽŽ—£³µµ²²´¹¼ÁÂÂÂÁÀ½¹®š‰ƒ‡•¢¨¬¬²¹½ÀÀ¾¼¹·¶º¾¿¸°³¶·º¼»»ºµ¤Œ…€=).////0..----+++,-.,*,,*,,*,,*,44-ƒ‚…ƒ€‚ƒ€€€‚„…‚~|~€€€€€€€€‚ƒ„ƒ€€‚ƒ‚‚‚‚€€€€„†{|}}}~~||}~„ƒ€€~ƒ‚€ƒ‚‚€‚‚€ƒ€€€€‚~€€€€~‚‚}~‚†‚~|~‚~zƒƒ‚‚ƒ‚‚‚€€‚‚€‚ƒ€€‚€€€€€€€‚€€€€€€€€‚‚€€‚‚‚‚ƒƒ‚€€‚ƒ…‚zwy{{zz{zzyx‚ˆ€€€€€€‚‚€€€€€€€‚ƒ‚€€‚ƒ€~€€€€€€€ƒ‚‚„„„}~…~tƒƒ„‚‚ƒ„‚€€€€€ƒƒ„€‚ƒƒ‚€€€€‚‚‚‚‚‚ƒ‚€‚‚€ƒ„ƒ‚‚‚‚‚‚‚‚ƒ‚‚€€‚‚……‚‚€ƒ‡€‚‹ˆ…€€€€€€€€€€€€€€ƒƒ‚€€€€€€€€€ƒ€~…ƒƒ€~ƒy„…………ƒƒ€€ƒ€ƒƒ‚€‚€€€€‚„ƒƒ„„„ƒ€€‚„ƒ‚ƒ„ƒ€‚ƒ€€‚‚€€€‚†…€ƒ„ƒƒ‚€…†ƒ€ƒ‚…†„„ƒ‚€~€€€€€€€€€‚€€‚‚€€€€€€€‚‚€„‚ƒ~€‚€„ƒ‚‚„ƒ‚€„ƒ‚‚‚€‚€€€‚‚‚€‚ƒ„ƒƒ‚€€ƒ‚‚‚€ƒƒ‚‚‚ƒ‚‚€‚ƒ‚€‚ƒƒ‚„‚€ƒ„„ƒƒƒ€€€‚‚wy€…††ƒ‚€€€€€~€‚€€€€€€‚€‚‚‚€€€€~€€€€ƒƒ~€€‚ƒ†„‚€€‚ƒƒƒ‚„„ƒ„‚‚‚‚ƒ‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚ƒ……ƒ€€‚‚‚ƒ‚‚ƒƒƒƒ‚‚‚‡ƒ‚‚ƒƒ„„‚€~€…†€~z|‰†‚€€‚‚ƒ€‚€€ƒƒ€€€„ƒ€€‚€~~‚€€€‚‚€€€‚„ƒƒƒ€~€€€‚ƒƒ„ƒ‚‚‚ƒ„„„ƒƒƒ„ƒƒ††ƒƒ„ƒ‚‚‚ƒƒƒƒ‚‚„‚‚‚ƒ„ƒ‚ƒƒƒ€‚‚‚€€‚‚ƒ‚ƒ‚‚‰„‚ƒ…‰‰……„ƒ†…€„‡ƒ}†…€‚‚€‚‚€ƒƒ‚‚€‚‚‚„„€€€€‚‚‚„‚€€„†„…„€€‚ƒ‚ƒ…ƒƒƒ‚‚ƒƒ„€„„ƒ„„‚„ƒ€ƒ‚‚ƒƒ‚€€ƒ‚ƒƒƒƒƒ‚ƒ„ƒ‚‚„„‚‚ƒƒ‚‚І„ˆ…ˆ‡…„ƒ€…‚††„€ˆƒ‚„ƒƒƒ‚€€€€€ƒƒƒ‚ƒƒ€€‚‚‚‚€€‚‚‚€‚‚‚ƒ‚‚‚„‚„„€€‚€€€ƒ†„‚ƒƒ†…ƒ‚‚ƒƒƒ‚‚ƒ„ƒ‚‚ƒ‚‚ƒƒƒƒ‚„„ƒ‚ƒƒ„„‚‚ƒ„…„ƒƒ‚ƒ‚‚‰„€€„‰‚‚ƒ€†‚†„‹‚„ƒ‚‚€€€€€€‚‚€ƒ‚‚‚‚‚‚ƒ‚€€€‚‚ƒ~ƒƒ€€‚…„‚€€…„„‚ƒ„„ƒ‚‚‚‚ƒƒ‚‚………„…‡†‚ƒ‚‚€€‚ƒ‚‚ƒ‚„…ƒƒƒƒ€‚ƒ‚ƒ…„ƒƒƒƒ‚ƒƒ„‚‚ƒƒ‚ƒƒ‚ƒƒ‚‚‰ƒ„‚‚€}‚‚‚‰ƒ€††ƒ„ƒƒ‚€‚‚ƒƒ€€‚‚ƒƒ‚‚‚‚ƒ„ƒƒ‚‚‚‚‚~€†„‚„‡†„ƒ‚€‚ƒƒ‚‚ƒ‚ƒƒƒ‚€††…†…ƒ‚„‚‚‚‚‚‚‚„ƒƒ„ƒƒ‚€‚„„ƒƒ‚‚ƒ„…„ƒƒƒƒ„ƒƒ„„ƒƒ„ƒƒ‚‚„…Š„ƒ„„€€ƒ}‚‚ƒ‚‰„ƒ„‚„‰‚ƒƒ‚‚‚€€€€‚ƒƒ€€‚ƒƒƒƒƒƒ„ƒ‚€€€€‚ƒ~ƒ‚‚ƒ……‚‚‚ƒƒ„„‚€€‚ƒ‚‚‚€€„‡‡‡†…„„‚€‚ƒƒ‚‚€‚‚ƒƒ„„ƒ„ƒ€‚‚ƒƒ‚ƒ‚ƒ„ƒ‚‚ƒ‚ƒ„„ƒƒƒƒ‚„ƒ‚…‹„‚ƒ„„ƒ‚ƒƒ‚„„Š…„„‚ˆ‡‚ƒ‚ƒ‚‚€‚ƒ‚‚ƒ‚‚‚‚€€‚‚‚ƒƒƒ‚ƒƒ‚‚‚‚€€‚€€€€„…††ƒ€€ƒ„ƒƒ†…„€€‚ƒ‚‚„…ƒ€€ƒ†‡…†„ƒ‚ƒ‚„ƒ‚ƒ‚‚‚ƒ„ƒ‚‚ƒ„ƒƒƒ‚ƒƒƒ‚‚‚‚ƒƒƒƒƒƒ‚ƒ„ƒ‚ƒŠƒƒ…†ƒƒƒƒ„„…†Œ„ƒƒƒŠƒ„„‚‚‚‚‚ƒƒ‚‚„„‚ƒƒƒ‚‚ƒ‚‚ƒ„ƒƒƒ‚‚‚‚€€€€€€€€‚††…„‚„…‚‚†„ƒ‚‚€€ƒ„„‚‚‚„……†ƒ„ƒƒƒ‚‚……‚‚ƒƒ‚‚ƒƒ‚‚‚‚„„„„……„‚ƒƒ‚„…†……„„„„ƒ„Š‚ˆ‡‡‚„ƒ‚‚ƒƒ„„‚†‰…††„„ƒ‚‚‚€€‚‚‚ƒ‚‚‚ƒƒƒ„ƒƒ‚‚ƒ‚‚‚‚€€€€€€€€€‚††…ƒ‚‚ƒƒ„ƒƒ„…„‚€‚‚€‚ƒƒƒƒ………†‡‡…ƒƒƒ‚‚ƒƒ„‚‚…„‚‚„…„„ƒ‚‚‚‚‚‚ƒ„ƒ„……„ƒ‚‚„„„„ƒƒ„……„„‚„Š€…‡ƒƒƒƒƒƒƒƒ‡Ž…ƒ‚ˆ„‚…†…ƒ‚ƒ‚‚‚€‚‚‚‚‚‚‚ƒ„‚€€‚‚‚€€€‚‚‚‚‚€‚…‡†„„‚‚†ƒ‚‚€„„…ƒ€€€‚‚‚ƒƒƒ……†…ƒ„„‚ƒƒ„ƒ„ƒƒ„„‚‚‚„„ƒƒ‚‚‚ƒƒ‚ƒƒ‚ƒƒ‚ƒ„„„……ƒ„„ƒƒ„„„„„…„ƒ†Š‚…†‚ƒ„„……„„ƒ†Œ„‚…‰ƒ„ƒƒ‚ƒ‚ƒƒ‚€‚‚‚ƒ„„ƒƒ‚‚‚‚‚€‚‚ƒ…„ƒ€„ƒ„ƒ‚‚‚€‚ƒ‚ƒ‚ƒ‚€€€€€ƒ‚‚‚‚……†„ƒ…†…‚ƒ„‚ƒ‚ƒ„ƒ‚ƒƒ‚„„ƒƒƒ„„„ƒƒ„†…„„„„„…„„„…„ƒ„„„„„……ƒƒŠ‡‡†‡‡†…†‡‡†„‡Œ„‰ˆƒ…ƒ‚‚‚‚ƒ‚„„‚€‚‚‚„…„ƒƒƒƒ‚‚‚‚‚€€€€€€‚…ƒƒƒ‚‚€€‚ƒƒƒ‚ƒ„ƒ€ƒ„ƒ„„ƒ‚ƒ……€‚ƒ€€€€‚‚‚ƒ„ƒ„……ƒƒƒƒƒƒƒ‚‚„…„„„ƒ„„‚‚ƒƒ„„„ƒ…†„„„ƒ‚€€€„‡ˆ†„………„„‚‚†„„………ƒ‚‚€‚€€‚ƒ‚‚‚„ƒ‚‚ƒ‚ƒ„ƒƒ‚€€€‚€€‚„„‚ƒ‚‚€‚‚ƒ‚€€‚„‚‚€€‚ƒ‚€€€ƒ‚€€€ƒ„‚€€ƒƒ„…„ƒ„„ƒ„„ƒ‚ƒƒƒ„ƒƒ„„„„‚‚ƒƒƒ„„ƒ‚ƒƒƒƒƒƒƒ„~{zz||}€~}|~€…Š‚ƒƒ„…„‚‚‚€‚‚‚‚‚ƒƒ‚ƒƒƒ‚‚‚‚‚ƒ‚‚€~€‚‚ƒ€€€‚„ƒ‚ƒ‚‚‚€€‚‚‚‚€€‚ƒ€~€‚ƒ‚ƒ…„„ƒ‚‚„…„ƒ„…„„…ƒƒ……„ƒ‚‚ƒƒ„„…†…„„ƒƒ„ƒ„ƒƒƒ„„ƒƒ„……„„……„ƒ‚zz~€…‡††…†‹„„„„ƒ‚‚ƒƒ‚‚ƒ„„…ƒ‚ƒƒ‚ƒƒ„„„ƒ‚‚‚ƒ‚€€€€€€€€€ƒ††€ƒ‚€€€€€‚ƒ„ƒ„‚€‚€€€€‚‚‚‚ƒ„‚ƒ…‚ƒƒƒƒ„††………„ƒƒ„„„„ƒƒƒƒ‚ƒ„…„ƒƒƒ„††……‚ƒ„„…†………„……„ƒƒ„…„„…„ƒ{{{}‚†…‚„„„ƒƒƒ‚‚‚‚„„……ƒ‚ƒ„ƒ‚‚ƒƒ„ƒƒ‚‚ƒ‚€€€€€€~€‚„‡†ƒ‚€€€‚‚‚‚€‚ƒ‚€€€~ƒ…„ƒ‚ƒ„„„„„„„ƒ„…„„…„ƒ„ƒ‚‚ƒ‚ƒ„…„‚‚ƒ………††ƒƒ„„„„„††…„‚ƒƒƒ„„ƒ„………„…ƒ‚}{~ƒ………„ƒƒ„ƒƒ‚‚„……„„„„‚‚ƒƒ„„„„ƒ‚€€€€‚‚‚€€ƒ…†ƒ€€€‚€€ƒ‚€‚‚‚€~€€~€~€}ƒ‚|‚„„„„„„ƒ„ƒƒ„„ƒ‚ƒƒƒƒƒƒƒƒƒƒ„„‚‚„†…„…„„„…†„‚ƒ„……„ƒ„„„„‚ƒ…„ƒ‚‚„…„ƒ„†‡†…„ƒ„ƒƒƒ„„„ƒ„…„…„„„ƒ„ƒƒƒƒƒƒƒƒ‚€€€€€€‚‚€‚ƒ‚ƒ~„……ƒ€€€€‚ƒƒ‚ƒ€…„€‚„…‚‚€€€€ƒ€€‚}|‚„……„ƒƒƒƒ„„‚‚ƒƒƒƒ‚ƒ‚„„ƒ‚ƒ‚‚ƒ„„ƒƒ„…„………„…„„„„ƒƒƒƒƒ‚‚‚ƒ……ƒ‚‚„„ƒ„„„…„ƒ‚‚‚‚‚‚‚ƒ„ƒƒ„„ƒ„ƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~€€€‚„ƒ‚€€„ƒ„‚€‚‚ƒƒƒ‚ƒ‚€€ƒƒ‚‚‚ƒƒ€€‚€‚€€€}}‚ƒƒ„„„ƒ‚‚ƒ„„ƒƒ„ƒƒ‚„ƒƒƒ„„ƒ‚‚„ƒ„ƒ„„„ƒ…††……………ƒ‚ƒƒƒ„„„ƒ„„„„„…„„„„„ƒ„……„„„„„ƒƒ‚‚ƒ„„…„……ƒ„ƒƒ„ƒƒ‚ƒ„ƒ€€€€€€€€‚‚‚‚‚‚}ƒ…‚‚‚‚‚‚ƒ„ƒ‚€€‚ƒƒ€~‚‚€€€€€~~{|}ƒƒ…†…„ƒƒƒƒ„ƒƒ„„„ƒƒ„„…ƒƒ‚‚ƒ„„ƒ„…„„…„‡‹†ƒ„ƒ„„„††……………„„„ƒƒ„……„„……††…†………„„„…ƒ„…„………†…„„…†…„ƒƒƒ‚‚‚ƒƒ‚‚€€‚‚€€€‚‚}„„ƒƒ‚ƒƒƒ…ƒ‚€‚ƒƒ€‚„ƒ€€€€€€~‚ƒƒ‚ƒ„†‡†…„„…„„„„„ƒƒ……„ƒƒƒ„„ƒƒƒƒ„„…†„‡Š„ƒ‚ƒ„…†Š‹Šˆ„„‰‹Šˆ‡†††‡†„„‡Š‰‰ŠŠ‰‰ŠŒ‰…„ˆ‹Šˆˆ‰ˆ‡„‚ƒ………„ƒ‚‚‚‚‚€€€€€€€…‚€‚‚‚ƒ„ƒ‚ƒ‚‚€‚‚‚„€ƒ€€ƒ}}€€‚†‚‚‚„„ƒ‚„†…„„„„ƒ…„„„„ƒ„†…„ƒƒƒƒ‚‚ƒ…„……„…†ƒ„ˆ‰‚}ˆ‰†…‡‰Š‰‰‡‚ˆ‡‚€‚ƒ‡ˆ‚€ƒ„†…ƒƒ‡‹‡ƒ„„„„ƒ‚‚‚‚ƒ‚‚€€€‚€‚~‚€‚ƒ€ƒƒƒ‚€€„…~€|{€€€ƒ€‚„†‚€€€€„…„‚‚ƒƒ„ƒ„…††……„„„„„…„„‚„…ƒ„„…†‡ƒ‚‚…‹Š…|~ƒƒ„‡…}€€€€‰…€…ƒ~z{|}|„€|€~|}ƒŽ‰ƒƒƒ„ƒ‚‚‚ƒƒ€€€€€€€€„‚€‚ƒ……ƒ‚‚‚‚‚€€€€{ƒ€~}€€€€€€€€‚ƒƒƒ„€€ƒƒ…†„ƒ‚ƒ……„„…„„„„„……„……„„ƒ„„„„„„…‰„ƒƒ…Œˆ‚|€„………Š…{|}{z{€‚…‚„|~~}{|€€~~„|x|ƒŽ‡„ƒ‚ƒƒƒƒ„‚€€‚‚€€€€€‚€€‚ƒ„…‚ƒƒ‚‚‚‚€€sz}}|€~€€€‚‚ƒƒ€ƒƒ{~„ƒ‚ƒ„ƒ‚„„„„„‚ƒƒ„…†…ƒƒ„ƒƒ…„ƒ„†„„„‰†‚†Š‡}|…ƒƒƒƒ‡…‚ƒƒ‚‚ƒ‚„„‰‡‚ƒ‚‚‚ƒ‚~ƒŠ‚ƒ‚{|Љƒƒƒƒƒ€€€€‚‚€€€€€€ƒ…‚‚„ƒ‚‚‚ƒ‚€rsx|}}€~€€€~€ƒ‚€~yw‚ƒƒ„ƒƒ‚ƒ…†ƒ‚„„„„††…„„ƒ‚ƒ„………†…„ˆ†„„‚}|…„ƒ‚‚‚‡‚ˆ†ƒƒ…„„„…„…ƒ††ƒƒ‚ƒ„„……€€…†‚……~~ˆ‰ƒ„„ƒ‚€€‚ƒ€€€€€ƒ‚€€‚ƒ…ƒ„„‚€ƒ„‚€sqvy|‚‚€~€€~€}}||{z‚ƒ„„…‡‡…„…††…†…„…†††„„„„ƒ„†…††…„†…€~€…†„ƒ„„„„ƒ…ŠƒˆŒˆ‰‰‰‰‡…ƒ†ƒƒ†‰‰ˆˆ‰‰‡ƒ€€…†ƒ„…ƒ„~‰†……‚‚€€€‚‚€€€€~€‚€~€‚„ƒ„„…ƒ‚ƒ€sqsux€‚‚€€~~~~‚}{}~}~|}ƒ…………†††††‡‡†††‡‡‡†……††…„…„…„ƒƒ‡…€ƒƒŒˆ‚…†……„‰‹…†ˆ††‡ˆƒ‚‡ƒ€‚„„……†‰…€€‡‡ƒ„„†„ƒˆ‚‚„„‚‚‚‚‚‚€‚‚‚€€€€~|~€€€„ƒ€ƒ…ƒ€psuvv{€ƒ|{|}~€||‚‚ƒ„„„„…‡†„„……†‡‡‡……„…„ƒƒ…„ƒ…„„‰„€„ƒƒˆƒ…‡†…ƒ‰‰}~~}}‚‚ˆƒ~}~}~~…ƒ€…‡ˆŠ‹‰…†‚ƒ‚ƒƒ€€€‚‚€€€€€€€€~{{€€€‚€€ƒƒƒ€|{osuuuv{€~‚€~{z|}|{~|z€‚„ƒƒ„…†…†‡…„……………†‡‡††„‚‚†‡†…„…‡„€€‚ƒ„„††…„„‚ˆˆ„…ƒ~}€€ƒ…~€€‚€~„‡„‚…‚€‚‚‚‚‚€€€~|~‚ƒ€€ƒƒ‚ƒƒ€}}rqrsxwxz€‚~~}|{{}~~~}|z}‚ƒ…„…††‡†††…„„†…„†‡††……††„…†††……†€€‚„„‚‰……††‰‡„†††‡†‡‡††„ƒ…„„…„„„ƒ‚ƒ„€€€‚ƒƒƒ‚‚‚‚‚ƒƒƒ‚‚‚€€€€€€€~~€‚„ƒ‚}}€„…„ƒ€stsuwttv|ƒ}}~}~}}|{}~|y|ƒ„„…†„ƒ…‡†…‡†…„ƒƒ………………†‡†„ƒƒ…†‡…~€‚†……ƒˆ‰………†ƒ‚„…†‡ˆ‡†‡‡„‚…„„„„…„„ƒƒƒˆ…€ƒ„…ƒ‚‚ƒ‚‚ƒ€€‚ƒ‚€€‚‚ƒ€€~|}}‚‚€~~~~€…„~€~uvvtsux{z|~|}}}|~~||||}|y{„†…ƒƒ‚‚…††‡‡†…†‡†………†…„…†…„„ƒƒ…ˆ…ƒ„†ˆ††ƒŠ††„„‡ˆˆˆ‡†…ƒ‚‚‡†…„„…„„ƒ‚„Ї‚„ƒƒ‚ƒƒ‚ƒ‚‚‚‚‚‚‚ƒ€€€€ƒ€||~~{|}~ƒ‚€€€~~€€€ƒ…€|}€‚vsstw{{}€~}~~~{||}|}~}}~{y‚…ƒ‚„…………†……„………†„‚‚‚‚ƒ„„„„ƒƒ†…‚€‚…†ˆ‡†„ƒ††…‚~ƒ„„…†ˆ†‚€€„†ˆ‰ŠŠ‹‰„‚…‹…‚„ƒ‚‚ƒ‚ƒƒ€‚€€€€€€~~}{{}}{}~€€€‚~~}~{~‚utuy}€~~}€~||~~~~~}}z‚†„…††††„„…††‡†„††ƒ‚ƒ‚ƒ„…†„„…„……ƒ„††‡†ƒ„„…‚~~~€ƒ†€„‚€ƒ…„‡‹ƒ‚…‰„‚„„„…‚‚ƒ‚€‚€€€€€€€€€€~~|}ƒƒ‚~~}}€~~€su{~‚€~~~}~~~}}€~}|}~„†ƒ„‡†…………†‡‡‡‡††„‚‚„…„„‡ˆ‡‡††‡‡†††………ƒ„ƒƒ„……„‚€€†…„…€€€€ƒ‡„‚€„…„ƒ„ƒ€‚‚‚ƒ‚€€€€€€€€€€€}|€„„€€€~„„‚€„ƒ„vzz|}|}‚}{|}~|~|{}~}~~{‚‡ƒƒ†‡„ƒ„„………†……„‚ƒƒ„„…†††‡†…†‡‡††‡‡‡†…„ƒƒ„„†…„††„„†„‚„…„„ƒ‚€‚€‚„…ƒƒƒ„„……„ƒ€€€€€‚€€€€€€€‚}€€€~}~€‚€€€€ƒ„ƒ‚€€‚†…ƒ}}{|€€||y}~}}~}||}}~~z€…ƒƒ„…„„„…‡†„„ƒƒ„ƒƒ„…†††‡ˆ†„„„…„„„ƒ„„„……ƒƒƒ„„„…††ƒƒ„„ƒƒ„……„ƒ‚ƒ„„…„„„„……„ƒƒ„……„‚‚~‚ƒ‚ƒ‚€€‚‚€€}~€}~€ƒ‚‚€€~€‚„~€€‚„„ƒ€~‚~|z{~~}}~~~~|}{~ƒ„†††††‡‡†……„„……„…‡††‡‡‡…ƒƒ„……„ƒ‚‚„„…„ƒ„„„…„„„„„…„ƒƒƒ„„„„„„…„„……††…„„……††„‚‚‚‚‚‚‚ƒ„……‚€€€€€€€~|ƒ‚€~~€ƒ‚€€‚ƒƒƒ~~~~}}~|~}~}}€~}~|}|~{z€‚…†††‡†……†…ƒ„………†……„††…††„†‰Š‡‡‡‡‡…ƒƒ„„„………„„„ƒƒ„„……„„„ƒ…††…„„…†‡†…„„„……„ƒ‚ƒ…‡†‚‚„„ƒƒ„ƒ„ƒ€€~€€€~{{~€€€~€€€€‚ƒ‚‚‚}}|{|}~|~}€}|{|}}{}}}~}~}|x~‚ƒ‚„„…†……„ƒƒ„„„„„ƒ‚‚ƒƒ†Œ‰‚€ƒ„ƒ…‰‰†„„„…„„ƒ„‰ˆ†„„…„…†…„…†††……†††…ƒƒˆ‡‡…€€€€ƒ„†…„„……„ƒƒ‚‚€€‚€€€~~~}z|~}}~€€‚€€‚ƒƒ‚~|z|~€~||||}|||{|}|||~~||}‚ƒ„„„„„„ƒƒƒ„………„ƒƒ„„„‰‹„€~€€ƒ‚„‹†„„„„…„‡‹Œ†„…„„……ƒ‰Œ††‡…„„„ƒ‚†‚‚…€„€‚€„†„„…„ƒ„ƒ‚€€€‚€€€~{{~}~~|€€€‚€€~{|€€}|~}~}|~||~~}}}~}~}~€‚‚ƒ„„ƒ„„ƒƒƒƒ„……„…†…††ŠŒƒƒƒ‚‚††ˆ†„……„ƒ‡Šƒ‚ˆ‡„†††…†„Šˆ‡„…††…†„ƒƒƒ~ƒ†‚ƒƒ‚€‡‡„„…„ƒƒƒ‚€€€€€€‚‚~z~|€~~~€€€‚‚ƒ…ƒƒƒ}}||z~}||{z~}}||}||~~~}~|}‚‚ƒ‚ƒ„„„ƒ‚‚ƒ„…………††…†‡‡Œƒ}€…‡………ƒƒ…‚„„……„„І„‰‚………†„ƒ‡ƒ…ƒ„…………ƒƒ†‚ƒ€€~††‚‚ƒƒ„ƒƒ‚€€€€€€€|}ƒ€{}}~€‚€€ƒ‚€€„……}~{{}z}€~}€}|~|}~{}~~|}~}|~ƒ…‚ƒ……ƒƒ„ƒƒ„…†††††…„…ˆ‰…†††………ƒ€‚………‡…††€€‰‡…„„…„ƒ…‚‡„†‡†……„‚‚€……‚‚„€€††„ƒ‚„„ƒ‚€€€€€‚‚{{‚€}€€€‚ƒ‚€€ƒ‚z}}}~|||}~~|}}|||{{}€~~~}}‚ƒ‚„„„ƒ„„……„……………†††…‰†€€ƒ…††‡……††……………††Š„ƒ‚ƒ‡‰…„„…„„…†‰ƒ†„„„„„„€€†ˆ‚„ƒ}ˆ†„„„ƒ„„„„‚€€€€‚ƒƒƒ„ƒy{€}€€~~}~€‚‚„……„‚‚~~~|}}{|~}~~~~}|{}}}€~||~€‚„„„„ƒ…„ƒƒ„„…†‡……††ˆ‡€‚„………………†‡†…†††‰‡…†€ƒ…ƒˆ„„†…ƒ„„†‰ƒ„††…„ƒ‚ƒƒ€„ˆ„…‡‚~ƒˆ…„ƒƒƒƒƒ„„‚‚‚‚ƒƒ…ƒ|z~}}}}~~~~~~~~„‡‡†„€€~~{|}~}||}~€~|}~}~€€€}}~ƒƒƒƒ„„„…„ƒ„„…†………†‡‡†ˆ„„‚‚„…††…†………………Ї€ƒ‚ƒ‚…‚‡ˆ†…ƒ††„‰…†……„„ƒƒƒƒ~€ƒ…Š€ƒŠ†ƒ‚‚ƒ„„ƒ€‚ƒƒ‚ƒ‚|€~}€~~~~~‚‚‚‚ƒ„‚~~~~}}{~}|}~~}{~}|}~~~~~~z|„ƒ‚‚ƒƒ………„ƒƒƒ„††‡ˆˆ‡„††…ˆ„…†††………‚„…„ˆ‹ƒ‚‚ƒƒ„…‡Š„„ƒ‚†ƒ‚…ƒ…†…ƒƒ‚„„‡‚€~†‡~€‚‹…‚‚ƒ‚‚‚€‚‚‚ƒƒƒ‚€}}~~€€~}~~~€€‚„~||}~~~~€~|}~}~€~}}~~}x|ƒ‚‚„„ƒ……„„ƒ‚ƒƒ„…†††‡†„‰†ˆˆ„‚ƒ„„‡‰„…†‰ƒ€ƒ††‡‡†ƒ‚†„ƒ„„‡…†…„„„ƒ‚ƒƒ„‰†€‚„Œƒ‚‹ƒ‚„„„„‚‚‚€‚ƒ„„ƒ„…„~|}~~}~~€‚ƒ~~€‚ƒƒ‚ƒƒ|{|~}}~~€€}~~|}~~~}}{|{~y|ƒ‚ƒ„„„„„„ƒ‚ƒ„„„„„†‡†…†ˆ„„ˆˆˆˆˆ‡†Š†„‹†ƒ„††‡‡†‚„ˆƒƒ‚…†…ƒ…†„ƒƒ‚†ˆ‹‚‚ƒ‰‡‚€‚„ƒ„„ƒ„ƒ‚‚‚ƒƒ„ƒƒ„…|}~}~}~‚„~€‚‚‚€‚†…ƒz{|}}|{}~|~}}~}}}~}||~~~~~z~‚ƒƒ„ƒ„………„„„„……„…†‡‡†…„„ƒ†ˆ‡…„…††Š‚ƒƒ‚……„„ƒ€ƒŠ„„‚†„ƒ‚ƒ…ƒ‚„ƒ€†‡Œ‚ƒ€„Šƒ€Œ…ƒ‚ƒƒ„ƒƒ‚‚ƒƒƒ‚‚ƒ‚ƒ„ƒ|~„€~€€~~|~€ƒ~€€€€€€€‚‚ƒ‚ƒ|z|~~}}~~}~|~€€~|||}}~}|~€€y~ƒ€ƒƒƒ‚ƒ„…„„………………†††…‡†ƒ„„„‚ƒ„…„‚…Œ‡‚‚‚ƒ‚‚‚…†‡‡„‚†‚ƒ†‡‡…„Šˆ€…„‹†„ƒƒƒ€ƒŠˆ‚ƒƒ„„ƒƒƒƒ„ƒ‚‚‚ƒƒ„„y€€~€€~}|~}€ƒ~~€€‚‚}~|{|}}|||}|||~}}~~||}~~~~~zƒ…ƒ„ƒ‚ƒ„„„„„„†……„…………„……†„ƒ„ƒ‚ƒƒ‚„…ˆ„‚‚ƒ……„ƒ„…ƒ‡‡„…ƒ‚ƒƒ‚ƒƒ‚ˆŠ€…ˆ‹…„„ƒƒƒ„~„‹ƒƒ‚„…ƒƒƒƒƒ„ƒƒƒƒ„…ƒ~y}~~~}||}€}|}~}€‚ƒ€|{{}~}||{{|}}}~~}~€~~}~}~~~zzƒ…ƒ‚‚ƒƒƒ„„„„„……„„„„……„……††…††„„……†…ƒ‚‚ƒ„…„„ƒ„…„…„ƒ†„„„„‚ƒ„„ƒƒˆˆ‚†ˆ‡ƒƒ„…„ƒ„„‚…ˆ…‚ƒƒ„ƒ„„„…„…„„„„„ƒ}x~}~~}~€~}~}}~}z{€~|zxy€€|{~|~~~}~~}}~~}~~~}}zx‚…‚ƒƒ‚‚‚ƒ„„ƒ„„…………„„„„………†‡‡††‡‡†…†…„ƒƒ„„ƒ„…„„†„ƒ‚ƒƒƒ‚‚‚‚ƒ„„ƒ‚ƒ„„„……ƒƒƒƒ‚‚ƒƒ„„„ƒƒ…„ƒ„…ƒƒ‚}~x|~}}~~}}€~}~€€~|z}~}|{{xusyy||€‚‚||}}}~}||~~~}€~{y‚†‚ƒ‚‚ƒƒƒ„„„„…………„„„…„ƒƒ…††††††…………ƒ„…„ƒ„„……„……„„……„ƒ„„ƒ‚‚‚„„„…„„‡†…ƒƒƒ„ƒƒ„„ƒƒ„………†…ƒƒ„„„„ƒ‚‚x}~~€}~}}~}~~€}zzyyzyyyvuuuuvwux|ƒ~{}}~~~}}}~~~}}{y‚†‚‚‚ƒƒƒ‚‚ƒ„……„…††…………………„„……†‡‡†……„„ƒƒƒƒ……ƒ„…„……„ƒ„„„„„„„……………„……„„…ƒ„………††…†‡‡†‡†…………„ƒ‚ƒx}~}~~}}}~~}~}||{{zyyyxwwvuswxzyy{€~}|}~|{||}||||}}~||€~yx‚…‚€€€ƒ‚ƒ„„……ƒ„…„„…†…………………†…………††‡†††„ƒƒƒ‚‚ƒ„„…†††„ƒƒ„……………††††……„„………†„„……………„†‡†…„…†……„„„ƒ‚~ƒ…w~€€~}}}~}}||{yyzywwwvuusxxz{x{‚~}|{}{|}~~}}}}~~€}~€{x……€€‚„………„……„…†……………„……„††……„„…††…ƒ„„‚ƒ‚‚ƒ„……ƒƒƒ„ƒ‚„„„ƒƒ…†…„„………††…„„„…†………„„…………………††„ƒƒ}~ƒƒv}€~~}}}~~~|z|yxyywxxuuvtxxyxx{€}|}|}€}~~}}zw‚††‚‚€ƒ„„ƒ„……„††…………†…†…„……†…„„…††††……‚ŠŒƒƒ…ƒˆˆƒ‚ƒ‡†‚‡Š‹Š†‚„„…………„„„„„„†‡††…„„…†‡‡†„„„„„‚‚}}u{~€~~~~}}~}}{{{zxwvxvussrux{xuy€~~}~}~}~}}}~~~~~}}}ywƒ…„‚‚€„„„ƒƒ………………………†„„…………††…ƒ„……„…………Љ‡‚„Š€ŒŒŒ„…††……………„„………††……†…„„„…„„„„ƒ~~€‚w|~~€€~€~~}~€~|{xxwvwwusrrwwxvvz€€~}|}~|}}}~}~}}|{{}wxƒ……ƒ‚‚„‚€€‚„„„„„„„„„„…†††††…‡†††„„…†„ƒ„„„‡‰„‰ƒ‚‡‰Œ‡‹‰‹‚ŽŽƒ„…ƒƒ„………„…†…††……„„ƒ‚‚ƒƒƒ„„ƒ‚€ƒ„‚}z~~~~~€~~~}~€~€|||yxwwwwuspqyxvtuz€€~~{}€~}}{|}|~}||}wzƒƒ†„‚ƒ„ƒ„…„„„ƒ……………††…††††‡†…„…………„„„„…ІƒŠ†€‡‰…Žˆƒ‹†‹‚‚€‡„…„………………†‡††…„„ƒƒƒƒ„……ƒ‚ƒ„‚€„ƒy|~}~~€}}€~~~~z{|zxxwwvsoszyxvvz‚|z||}€~~€~||~~~w{ƒ…‡ƒƒƒ‚…†‚„„„……†…„………†…„„„„…‡………††…„ƒƒ‚†‰„ƒ‡ˆ„†ˆ†‹†ˆ‚„Š‚€…އ„„„„„„„„…†…„„„„„ƒ‚ƒ„†††„ƒ‚€€€‚‚‚{{~}}}€€~}}€{|~zyzzvsrrsyxxyyz€}||{}~}|}~{|~~}}x{‚…‡„ƒ‚‚ƒ„ƒƒ‚‚ƒ„…†………†††…„„„„„††…„……††††…Šˆ††‡‡‡‚†‰„‚ˆ‡ˆ„„Œˆ†‹Ž‰„„†…„ƒƒ„„„„…„…†…††…ƒ…†‡††„„~…ƒ‚‚ƒ{{~~~}}~~€~~~}zzz{ywvtuttxxwwvz~z|}~|}}{|}}|}‚€‚€y}‚ƒ…„„‚‚ƒ‚‚ƒ€ƒ„„„„„……†††…„„„„†‡‡…†…‡††‡ˆˆ……ƒƒ†…„‡‡„ƒ„†…„‰‰‹Žˆ‚„ˆˆ††……††…„„„…„ƒ„…„„…‡†„ƒ„ƒ€€‚„…„‡}|~~}€~}}~~}~}|zxxxvvustyxwwwz}€||}~~~|}}|{|~‚}w}ƒ‚…‡†…„„ƒƒ‚€‚„„ƒƒ„„†‡††††………†††…………………†……††……„„„…„††„ƒƒƒ‚ƒ……„„†‡†‡‡‡‡††…„„ƒƒ„……………†……„ƒ€€ƒ…ƒ„{|~}€€}~}~~~~}zzyzzxvvtsxxxwwy|~~~~~~}~}|~}}||}~~~w|ƒ„†‡……††……ƒ„ƒƒ…„„„…††…††…„„„……††††‡‡†……†‡ˆˆ‡‡‡‡††††ˆ‡††…„…†…†‡ˆ‡‡ˆ‡‡‡‡††‡†…ƒƒ„…†‡‡†…………ƒ€‚ƒƒƒ„‚€{|€~~~€€~~}~€{y{yyxwvvvvwxxuuv~€€~}}~||}|}}€~~€€}w}„†……ƒ„†„„ƒ„„‚„…„„„……„…………„„…†††‡ˆˆ‡‡‡†‡‡ˆˆ‡ˆˆ‡‡†ˆˆŠ‰‡†††‡‡†‡‡ˆˆ‡†††††……†…ƒƒƒƒ……†‡††……„‚€€…ƒ†y}€~~}€€~~~€~|zxxxxxwvxxvuussz~}}~~~~~|}~€€}}}|u}„„„„ƒƒ„†…„ƒƒƒ‚ƒ„……„„……„††††…„…†…†‡‡†„„„ƒƒ‚ƒ„„„……†††‡‡‡††‡………„…†‡†……††††…„„„ƒ„…‡†††……††…‚ƒ…ƒƒ……„€ƒƒxz~~}}|~~€{~}zyxxxvssuttwus{}~~}~~}|~~|}}|~€|w„…„…ƒ„„„ƒƒ…„ƒƒ‚‚ƒ„…††††‡†‡‡†‡‡†……†„ƒƒ„„ƒƒ‚ƒ‚ƒ…†…„…‡…„ƒƒƒ‚‚‚‚„……†…„„…†„……†††††††††…ƒ€ƒ†„„†…‚ƒ…zx}}€€}~~}~~~}€‚}{|{zywvurovutvwxy€~}|~~~€~}~}~|~}w……ƒ‚„„……ƒƒ„…ƒ‚‚ƒ‚ƒ„……†††‡‡††††…„„………ŠˆŠŠ‰‰‰ŠŠ†‚„†…„„†…ƒ‰‹Šˆ†ˆŠ†‚ƒ„„„ƒƒ‚…‡††††…†…††…ƒ€…„ƒ‚„…ƒ‚€…ƒzz|~~~~€}}~~€ƒ}x{|zyywttpvuvrswz~~}||||~€€}~~~~~w~„ƒƒƒ„„†…„†…„ƒ‚ƒ…†…ƒ„………††††††††††………‡…„‡‡‰‰‡†‡‹„‚„ƒ…„ƒ‰Œˆ†…††…†‰‹…‚„„„…„„††‡†…„……………„‚ƒ„„„„………‚‡‚z{}}€~}}}~}}€~‚}{z}|zyyvurnqtvttww|}~~~|}~}~~}~~€~x„„„……ƒ„…„…„„‚‚…‡†…„ƒ„„……„…†‡‡††††…††ˆ„ƒƒ„„…ˆŒ……ˆ‚ƒƒƒ‚ˆŠ†‡‰‰Š‹Œ‰…‡Œ†ƒƒ„††††…†…„…††„„„‚ƒ€‚ƒ„†…„…€y{€}€~|~}}~~€‚€}{zxvxuquuwvvvv||~~~~~~~€€~~~}w……„…‡…‚„††…„ƒƒ„…†…†„‚„………………†……††††‡ˆ…ƒ€‚ƒ‚†ˆ„‡ˆƒ…ƒ…†‰Šƒƒ„ƒ†‰„‡‹‡†……„…‡†…„…†††„…ƒ€‚ƒƒ‚ƒ…†„€„†€y{~~~~€~|€€€~||zyywtpwyyxwxz~~~€~~€€€€}}~~~}v€„ƒƒƒ……ƒƒ„…††††„‚‚ƒ…„ƒ„„…†……†…„††††‡‡„†€ƒ…†…„‡Š†Š„„…‡ˆ‰‚ƒ…†……ƒƒ…„ˆ‰††„ƒ…‡†„„…††……„‚‚ƒ„ƒƒ„„ƒ‚……y|}~~~~~€€}}~€~}}~~{zxzzvqovxyxxwuz~}~~~~}~~~~~z€‚‚ƒ„„„„ƒ„…††…„ƒ‚ƒƒ„„„„„………‡‡……†………ˆŠ†ˆƒ†‡††…„‰…‡ƒˆ†ƒŠ„ƒ†……„„…ƒ„ƒ…ˆ………………†„„…………„‚€‚‚„„ƒ‚ˆƒy{|}}}~}}~~}~}}}{|zyyxvryyxyzywz|}~~~~~~~~~}~~€z~ƒ„ƒ„…ƒ‚ƒ„†„„‚‚ƒ„„…†††„ƒ„„…†……††††…‡‹†ˆƒ…†………ƒ‡ƒ††€‰„†Š…………„„„ƒƒ„„…Š„…‡†††‡†…†…„„‚€ƒ„ƒ‚‚„„„„…„†‚€x{~~}~€€~~}}}}{y|yxu{zyyyz{{}~}~~~~€}}}~€€z€‚‚ƒƒ„†„„„„……†………†……„„††„‚„…†††††„‡‹†ˆ‚……„„„ƒ…€…ˆ‚‡„ˆ‡…‡‡……†…ƒƒƒ†‹„………‡†‡†‡…ƒƒ‚€ƒ……„„…†…‡…ƒ††ƒ~v}~|}}~}~~~€}{}}~}|zx|{xuyyz|zxxy}~}~}}~}~}{~~~~}{€‚‚‚ƒƒ††„„„„…†‡†………„ƒ………ƒ„„……††††„‡‡†‡‚„„„ƒƒƒ‡‚†ˆƒ‡„‡…‚‡†……†…„„€„ˆ‰…††‡‡‡‡††…„ƒƒ„„‚ƒ‚„……„„„‚‡†„{~~~~|~~~}}~~€|}}}}|z|zywwwwyzxxz~}}}~~~~~}€z|€ƒ„„ƒ†„‚‚ƒ…††„„†……„††ƒ„†…ƒ„„„……††…‰ˆ…ˆ‚„„„„ƒƒ†‡…†‡†ˆ‚…††………†‚ƒ†‹ˆ…††‡‡‡†…„„„‚‚„„‚„ƒ„…„…ƒ€…„„y€€€€~}}~~}‚|}}~}~€~{}zuuwyxxx{}|}}}|}~}|€~~~~~~~{ƒƒ…„„„……„„ƒƒ„„„…†‡†††…††‡†……ƒƒ„ƒ„…†…‹‰†ˆ‚„…„„„‡…„ˆ‚…‰†Šƒ‚…………„‚††Ž…„†††…†……„ƒƒ‚‚„…„ƒƒ„„„…‡…‚€„††ƒz€€~~~~}}~}~€‚~}}}}}}}~}}{xvxyyzyx{|~|€€~~~}€~|~}{ƒ„ƒƒƒƒ„…†…„ƒ„„„„„…………†……‡‡‡‡‡…ƒ„„………ƒ‹††‚€ƒƒƒƒ‡‡ˆ‰ƒƒ„‰††‡‚ƒ„„„€€…‡‹Šƒ…†……………„‚ƒ‚‚ƒ………ƒ„…„„…†„ƒ‚‡‡†ƒ{}€~}~~~||~€€~~~|zy|~}{yxxz{zz|}|}}~~~~}~€€}|}z}…ƒ‚‚‚ƒƒ„…†„‚ƒƒƒƒ…………†‡†…‡‡‡‡……†……………ƒŠ„ƒƒ„‚ƒ†‡„…Š……†…ˆŠ‚‡‡„„ƒƒƒ††‹ƒ„†‡‡‡†…„ƒ‚ƒƒƒ„††……„††††…ƒ…ˆ†…ƒ€~~}€€‚€}|~€€€~~}{yyz{xy}€}|{zxyyy}~|~€~~}}}||}}~z~…ƒ‚‚‚ƒƒ„††…ƒ‚„…………††…†††„ƒ‚…‡‡…………ƒ‰…„…‡†…†††‹‹„…†††…ˆˆ„…‡†………†Š‡„†‡‡‡†‡„‚‚ƒ…„„……††……………„ƒ†„„‚€}~~€€€~||}€€~||{zy{}zz{~yzzxvyxx{}|~|}~}~}}}}zz…„ƒƒ‚‚‚ƒ…††…ƒ‚ƒƒ……„„„……†…„……†‡†„„††„Љˆ‡‡‡‡‡ˆŠˆ…„†‡‡†…ƒ‡Š‡„ƒƒ„…Žˆ†‡‡‡††……„‚ƒ‚„„ƒƒ‚ƒ†…††……†ƒ‚…„„‚€‚€€~}€}~~~}~}~~~}||||}|{~}|{yx}}yz||}}~~~~|}{{ƒ‡‡ƒ‚‚ƒ„„††„ƒƒƒƒ„……„ƒ„………†ˆ‰‡ˆ‡…„†‡††‡‡†‡ˆˆ‡‡†……‡‡‡‡‡††………ˆŠŠ‹Œ‹Š‡†‡‡†††…„ƒƒ‚„„‚ƒ„……„………†‡†…„†…„„„‚ƒ}}|~~~~‚„‚€~||~{z€{||{{yz|}zy~}~€~~€~~€y|„…†ƒƒƒƒ„„†……„‚‚ƒƒ‚‚‚„…†‰‰‰‰‡‡††‡†††††‡‡…ƒ„„…†‡†………††††…„„………ƒ„†††††††„ƒƒ‚€‚…†††„„…………„€„ˆ…„ƒ‚„„}~~€‚}}€‚ƒ€€~|}||||{{{~|}{{zyyzzzz~}~~}|~|~~||}y~‚„…‚‚„ƒƒ„„„…††…‚‚‚‚…‡‚„………‡‡ˆˆ‡ˆˆˆ‡††‡‰Šˆ‡‡††‡‡†…„…††……†††…††††‡†††…„„ƒ‚~}€ƒ…ƒƒ……„………‚†‡†„ƒ‚„ƒ~‚~~~}~€€~~€~||}}zy{||{{|zyzxxz{yz|~~}}~}~~}~~}}€|z€€…†‚‚ƒ„„„„…††………ƒƒ‚‚ƒ‰††…„„„ƒ‚ƒ‡‡ˆˆˆ‡‡‡ˆˆ‡‡†††………†…††‡†††‡†††ˆ‡††††„‚‚‚~~‚‚ƒ…„„„ƒ…†…„„„……†……ƒ…„ƒ€}~~~}~~}~€~}}~||}|{z||z|}}|}~{}}~{}~}€}~€~z|„ƒ„†‚‚ƒ„……„†‡‡†‡…ƒ‚„ƒ„ˆ€€‚ƒ„‚„‰††„‚…†‡†††‡‡††…„……………†††‡‡‡††‡††…„ƒ„……„‚‚ƒ|…………„Œ‰……‡‡…„„„„‚†††…„„„‚€}€~}~~~~~~€~~~}|z|}}}z||~~~}{|}}€~~~}~‚}}x}‚„„†‚‚ƒƒ„„ƒ„‡‡†‡„‚‚ƒ†‡~}‚ƒ„‹‡†ˆ…‚…†‡‡†…ƒƒ‚‚††…†‡††‡‡††‡‡…ƒ€~€‚‚„„€ƒˆ‰ƒ~†ˆˆ‡‰‹Ž…ƒ…††…„„ƒ…‡…„…„‚ƒ‚€|€~}}~~~}}~~~~}}}}~~~|{}}}~€}}}€~~~~~}}{€…ƒ‚„‚„„„„„…†…†‡…‚ƒ‚ƒˆ…~~|ƒƒŠˆƒ…„„…†††‡„‚‡‡‡„„ƒ„††††††…ƒ€‚„„ˆ‡‡‰‹‰„‚€…††‰……††‰ˆ„„ƒ……„„…‚~‚††…ƒ‚‚‚……ƒƒ{€}~€‚€}|€~~€||}}~}}€}~|}|~~}~}~}{z„ƒ††‚‚ƒ„ƒƒƒ„†††††………„…ƒ‚‚~‡Šƒƒ†‚„…††††ƒŠ††ƒ‚…†‡‡††‡„€…†‘‘ŒŠ„Š‹€…{‰†…†…„„ƒ„„…††…„„‚‚„††…„ƒ‚…„‚€€}~€~}~}}}~~~}}}}||}|}}|||~~~}~}||~~}€}|~xz‚ƒ„††ƒƒƒƒ„„„„†‡†††‡†„„†ƒ‡‚€„‰„‚…ƒ………„„ŠŠƒ‚„†‚‡‡‡†‡†‚ˆ‹‹ˆ†……Љ€‚†|†‰‡‡††…„ƒƒ„†…„……„‚ƒ…††…„„‚ƒ„‚€€‚~|}}~~€€€€||~{|}|{|}{€~~~{}~}|~}€~€~}~|‚ƒƒ„…ƒ‚ƒƒ„„ƒƒ„…†††‡…„ƒ……}†„}ƒ†„…€ƒƒ…†…‚ˆ‰‡ŒŠ„ˆ…†††‡„‚ŠŒŒ„‚„„„„„ŠŠ‚‚†‡€‰†…†……„ƒƒ„………††…ƒƒ…‡…„ƒ„„ƒ††„‚ƒ~€~~€€€€~~}{}~~~~€}~|~~}€€}}~~~}||}‚„„„„†ƒ‚ƒƒ„ƒƒ„„…†††‡††‡‡††‚ƒ„…„ƒƒ„†ƒ†‹…І‹†††„††‡†ƒ‹‰‰ƒ„„…†…„ƒƒƒƒˆ‡€Š‡†…ƒƒƒƒƒ„„……………ƒ…‡†„ƒƒ„ƒ„…„„„ƒ‚~~€~€€~{~~€€~~||€€~~~€‚€€~~|}~~~{|z}„„ƒƒ…ƒ‚ƒƒ„„…„…†„„„„…†‡‡ˆ€|‡…€„„‰ƒ€„„„„ƒ‰…ˆ…€ˆˆ‚‰ƒ„††„†‡†Š‚„……†„ƒ„‚€‚‚„‰„|‰‰……‚ƒ…„ƒ†‡†……„ƒ‚„††„„……‚€„…ƒ‚ƒƒ‚}‚€~~}€|€€€~}~~~~~€‚€}}ƒ~€~€~}~}zz€„…„„††„„ƒ„„„ƒ„…„„ƒƒ„‡‡ˆ‹„~…‡„…ˆ‹ƒ„„ƒ‚ƒˆ†ƒŠ…ˆˆ…ƒ„‚‡…„ˆ‚„…„ƒƒ€€ƒ‚ƒ‡…~ƒˆ‡‚„‚ƒ…‚‚ƒƒ„„…‚„‡†„„„„‚€„„‚ƒƒ„ƒ~‚ƒ~‚€€€€€€€||}}|z~‚€~~}}}~~~~~}}€‚ƒƒƒ„…ƒƒ„„„„ƒƒ…††„„…††‡‰„…‡‡†ƒ…‡‚ƒ„†„…‡ƒƒƒ€‚†„…†‚ƒ†ƒ…†ƒƒ„„ƒ…ˆ…„‚ƒˆ‡Š€„…ˆ€€ƒ„‚|‰Œ…„…„€ƒ„…†„…†„€„ƒ„…ƒƒ‚‚~~€€€€~~~~}|{||}‚ƒ€€€€€„…€}~~}}~|}‚ƒƒƒ„„…ƒ‚ƒ„„…†…††……†……„„ˆ‡‚ƒ‡Œ‰‚…ˆ‚„†„†…€€|~€€…‡‚ƒ‚‰„ƒ†‚ƒ††ƒ…‰†ƒ€ˆ€€†‰‚†ˆ†‰€ƒ’Œ„„ƒ‚„………„†‡„€„†„„„ƒ‚€~~‚€}}|{|}~||{{~€ƒ‚€ƒ‚†|~~~}|~€ƒƒƒ„„„†„‚„„„………………†††…ƒƒŠ‹ƒ„‡‰ˆ†‡‰‚€ƒ„ƒ…„€€‚ƒ„‚…„ˆ‡€…ƒ††„†‹††„Š€‡Š„‚‰ˆ††“ƒ……‚„…††……‡†ƒƒ†‡††…„„ƒ€~‚ƒ~}|||~€€~~~~~‚‚€‚‚…~~~€~|€‚€„ƒƒ„„ƒƒ……ƒ‚‚ƒ…………†††……„ƒ‡Š„…‡………‡‰„ƒ…†ˆ†‚ˆ‡‡ˆ‡†……ƒ„‚„…†‰‚ƒƒ…††‡‡„‡‡Š…‚ˆ‰‚‚ƒ‹…‚€‚”…ƒ…ƒ‚ƒ†††………†„‚ƒ††„„„‚ƒ„ƒ€€€~€~~}~€€€}|}~}~€ƒ‚€€~…ƒ}}|}}‚€„„ƒƒƒ„ƒ„…‚‚‚ƒ„„………††„„„„…„ƒ…………ƒ†‰„‚ƒ„†ƒ†ˆ‡Š‹Š‰ŠŠˆƒ‚‚††‰‹ƒƒƒ‚€€€ƒƒ†‰…„ˆˆƒ…ƒŒ‹ˆ‰Ž†…‡…‚„†……………††‚€‚‡†„…‡…‚‚ƒ‚€€‚€~~~~€~}}~~{|~‚€€€€„ƒ{|~‚€‚ƒƒ„‚ƒ„„ƒ†‡ƒ‚ƒƒƒƒ…††††…„……††††…†‡…†Œ‰‡…‰ŠƒŠ†††‡‡†……‡†„‡‡Œ‹ƒƒƒ}~€ƒ‰„„†‹…„ƒ‚„ˆŠ‹‰„„†…‚„……‡†…………‚ƒ„…„………ƒ‚‚…ƒ€~~|~€€}~~{{~~€€~}€€ƒ‚€€‚€…}{}ƒ……ƒ‚„ƒ„„…‡„‚‚ƒ„„……†‡…„………†‡††…………†ˆ‰ˆ†ŠŽŽŒˆ††‡‡‡‡‡†Š‹ŠŠ„†‡‡‰Œ…ƒ€‚ƒ‹€„ƒƒ……ƒ‚„††…†…‚ƒ…‡‡††……‚……†††…„„ƒ‚‚€ƒ…ƒƒ‚~€ƒ€€€€€}}€ƒ~}|~‚‚€€‚~}~}€…„‚‚‚ƒ……‡‡„‚‚ƒ„……„„…‡††……„„†„„…„„…†††……„…†‡‡‡‡‡†…†††‡ŠŒ‡†††…†Œ‹††‡‰‹ˆƒ‚ƒ†‹‡ƒ„„…„…‡ˆ†…‡„‚…„…………†††‚††…„„„ƒƒƒƒ„ƒ€‚‚‚~€€~‚ƒ~}}}~‚€€ƒ€~‚„ƒ‚……‚‚„„…‡†„‚ƒ„ƒ„ƒƒ„…†‡‡……†‡…„…„„…†††††††………†…„„†‡‡‡††††††††‡††ˆ‹‹Œ‹‰„‚…†…ƒ‚ƒ„………†……††…€……†……„„…†„‚ƒ†‡†…„ƒƒƒƒ„†ƒ‚‚‚‚€~||}~~}~|y„ƒ‚€€~ƒ‚‚~|€„…ƒƒƒƒ„…„„‡‡†ƒ‚ƒƒ‚‚‚ƒ…††……………„„„„…††……†‡†…„„ƒ„„…†‡††‡‡‡ˆˆ‡‡†††††‡†…ƒƒƒƒ„…„„ƒ……„„…†‡………„€„…†‡†„‚ƒ…†ƒ‚„††……„„ƒ‚‚ƒ„ƒƒ‚‚ƒ|{|‚~zz}{|}{€€ƒ‚€€€|~€„€~~…„ƒƒ„„„ƒƒˆˆ†ƒ€ƒ…ƒ‚‚„…†…„ƒ„……„„…†…„„„………„„ƒ„†‡‡‡‡††ˆ‡†††……„ƒƒ„„…†…ƒƒ„„…†…„ƒ…††…†‡†††…‚€ƒ„…†…„………„ƒ‚…‡†„„„„ƒ‚‚ƒ…„‚„€{{|~€€~||€|{z|€„€}€‚‚€€€„€€„†„„‚‚ƒ„„ƒƒ†‡ˆ‡„ƒ‚ƒ……ƒ„„…………„„„„ƒƒ„…„……†††„„……„ƒ…‡‡††††‡†„„„…„ƒ‚ƒƒƒ‚‚‚„………„„„„†‡†††††††…ƒƒ„ƒ…‡†…†‡‡…„ƒ‚…†„‚ƒƒƒ„„…„…†„ƒ€ƒ„€€}{~~}|~~}}~{|€€‚€‚€~€€€…ƒ€‚…‡…ƒ„„ƒ‚…†‡††…‚„…„„„…………††††„ƒ„„„„„„„……„„…………††††††‡†……„„…………„ƒƒƒ…††……„„†‡‡††††††‡†€„„„†‡†…†††…„€……ƒ„…ƒ„††…………„ƒ…€~€}~~{z~}{{~~~ƒ€~€€€~~~†ƒ€ƒ„„‚‚ƒ„…„ƒ‚ƒ…‡†‡†‚ƒ„ƒ‚ƒƒ„„…‡‡‡‡‡†‡†…………†…„„„…‡†‡‡‡‡‡†………†„‚„„‚‚ƒƒƒƒ…‡†……†……‡††…‡‡‡††„‚‚ƒ„…‡‡…„††…‡†ƒƒ‰‡„„…„ƒ„…„„†‡…ƒ‚€„€€€~~~~|~‚€€|ŒŒŒ‹‹‰‰‰ˆ‰‰‡‡‡ˆ‰ˆ‡ˆˆˆ‡‡ˆ‰‰Š‰‡‡ˆ‡ˆ‰ŠŠ‰‰‰‰ˆ‡ˆˆˆˆ‡‡‡‡‡ˆˆˆ‡‡‡‡†††††††††††…†…„ƒƒ‡‰†††ˆ‰‰ˆ‡‡†‚‚„††……………†………†††‡‡……‡‡‡†‡‡‡‡‡‡‡ˆˆˆˆ‡ˆˆ‡‡ˆ‰Š‹Šˆˆˆˆ‹Šˆˆˆ‡Š‰‡‡ˆ‰‹ˆ‡‰Š‰‹‰Ž’ŒŒŒŒŒŠ‰‰ˆ‰Š‰ˆˆ‡‡‡‡‡‰‰ˆ‡‰‰‰ˆˆ‰‰‰‰ˆ‰ŠˆˆŠŠ‰‰‰‰‡‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡††………†……†††…………ƒ‡Š‰‰ŠŠ‰‰‰Š‰‡Š…€„………„„„…†††…††‡‡†…††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰ˆ‰‰ˆˆ‡‡ˆˆ‰ˆ‡ˆ‡ˆ‰‰‡ˆ‰ŠŠ‡‡ˆˆ‰‹ˆ‰“ŽŽŒŒ‹‹‹‹ŠˆˆŠ‹ŠŠŠˆˆˆ‡ˆ‡‡‡‰‰Šˆ‡‰ŠŠ‰‰‰‰ˆˆ‰‰‰ˆˆˆˆ‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡‡…………†††………††…„‚…†………„„„„„„‚„„€ƒƒ„„„„„……†††††……†††††‡‡‡‡‡‡‡‡‡‡‡‡ˆ‡‡ˆ‰ˆ‰‰‰‰ˆ‡ˆ‰‰ˆ‡ˆ‡‰‰‡†ˆŠ‹Š‡‡‡‰Š‰ˆ‰Œ‹Œ‹ŠŠŠ‰ˆ‡ˆŠ‹Šˆ‡ˆ‹Šˆ‡‡‡ˆˆˆˆˆˆ‰‰‰‰Š‰ˆ‰‰‰ˆ‡‡ˆ‡‡‡‡‡‡‡‡‡‡‡‡‡††††………††††…††…„„ƒ„†††…„„„…„†…ƒƒƒ‡‚‚„…„…††……………„…†‡††‡†††‡‡†††‡‡‡‡‡†‡‡ˆ‰ŠŠŠ‰ˆ‡ˆ‰ˆˆ‰‰ˆ‰ˆ‡ˆ‰ŠŠ‰ˆˆ‡ˆŠ‰ˆˆ‹ŠŠŠ‹‹ŠŠ‰ˆ‰‰ˆˆ‰ˆ‡‡‡‰‰‡†‡‡ˆˆ‡‡ˆˆˆ‰‰ˆ‰‰ˆˆ‡‡‡‡ˆ‡‡†‡‡‡‡‡†††‡††††††††……†††…†…„„……„………„„„„„†ˆ‡†ƒ„‹‡‚‚‚‚„…†††††………††…††…†‡‡‡‡‡‡†‡‡†‡‡ˆˆ‰Šˆ‡‡ˆˆˆˆˆˆ‰‰Šˆ‰ˆ‡ˆ‰‰Šˆ‡ˆ‡ˆŠ‰ˆ‰‹ˆ‰‰‰Š‹ŠŠ‰‰‰ˆ‡‡‡‡‡‡ˆ‰ˆ‡‡‡ˆ‡†‡ˆˆˆ‰Š‰ˆˆˆˆ‡‡ˆ‰ˆˆ‡†‡‡‡‡‡†…†††…††…††‡†„„„…………„„ƒ„…„……„ƒ‚…†††„‚„†‡Š‰‚„………†††…†††‡……†††‡‡‡‡††‡‡‡‡ˆˆ‡‰ŠŠˆ‡ˆˆˆˆ‡‡‡ˆ‰Š‰ˆˆˆ‡ˆ‹‰ˆˆˆˆ‰ŠŠ‰‹‹‹‹Š‰‹Œ‰‰Š‰Š‰‡‡‡‡ˆˆ‡‡ˆˆ‡‡‡‡‡‡ˆˆˆ‰‰‡‡‡ˆˆ‡‡ˆˆˆˆˆˆ‡†††‡‡‡‡‡†‡††……………„„„………„„„‚‚………„ƒ€}~€‚ƒƒ…ƒ‚„ƒƒ†„ƒ„„„„………„„……………†††‡†††††††††‡ˆ‡ˆˆ‡‡‡‡ˆˆ‡‡‡‡‡ˆˆ‰‡†ˆ‰Šˆ‡‡‡ˆŠ‰ˆŠŠŠŒ‹‹ŠŠŠˆˆ‰‰‰‰ˆ‰ˆ‡‡ˆ‰‡‡‡‡‡ˆ‡‡‡ˆˆˆˆˆ‡‡‡‡‡ˆˆˆ‡‡‡ˆˆ‡††‡‡††††…†………„„„„„„„„„„„„„‚†…„„~~‚ƒƒ„†„ƒƒƒƒ‚‚……†………„„„„„……………………††††‡†…††‡ˆˆˆ‡‡†ˆˆ‡ˆ†‡‡‡‡ˆ‡‡‡†ˆ‰Š‹ˆ‡†‡‡Š‰‰‰ŠŠŒ‹Š‰‰‰ˆ‰Š‰‰‰ˆˆˆˆ‡‡‰ˆ††††‡ˆˆ‡‡‡‡ˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡†††………„…………„„„„„„…„„„ƒƒƒƒ„‚€†„ƒ„„€€ƒ‚‚ƒ„ƒ„ˆˆ„ƒƒ„„………………„„„„††„„…………„…†‡‡††††‡‡‡‡‡††‡ˆ‡‡ˆ‡‡†‡ˆ†‡ˆˆ‰Š‰ˆ‡‡‡ˆˆˆ‰‰‰ŠŽŒ‹Š‰‰‰‰‰‰‰ˆ‡‡‡‡‡††‡…………†‡‡‡‡‡‡ˆ‰‡†‡†‡‡‡‡‡‡‡‡‡†††††………………„„„„„„„ƒƒƒ„„„„„„‚‚ƒƒƒ††‚……ƒ‚‚€ƒ…‡…ƒƒƒ„„ƒƒ„„„ƒƒ„„……„„…………………†††…†‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆŠŠ‡†‡ˆˆˆ‡‡‡ˆ‰‰Š‰‹ŠŠ‰ˆˆˆˆ‡ˆ‰‡††‡‡†…†……†‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡†‡‡‡‡‡‡‡†…„„„ƒ„……„„„„ƒƒƒ„„„ƒƒ„ƒ‚ƒƒƒ„†„†…‚‚ƒƒ€€ƒƒƒƒ‚„„„„„ƒ„ƒƒƒ„„„„„„„…………„„„………†‡‡‡‡‡‡‡†‡‡‡‡‡†ˆˆˆ‡‡‡‡‡‡Šˆ††ˆˆˆˆˆˆˆˆŠŠŒŠ‹‹ŠŠŠˆ‰Šˆˆˆˆ‡‡‡ˆ‡‡‡‡ˆ‡†‡††‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡‡††‡††…„………„ƒƒƒƒ„…„ƒƒƒ„„„„„ƒ‚ƒƒƒƒƒƒƒƒ…ƒ‚‚‚„‚€‚‚ƒƒƒ„ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„……„„…………††‡†‡‡‡‡†††‡‡‡‡‡†‡‡‡‡‡ˆ‰ˆˆ‰‡††‡ˆ‰‰ˆˆˆ‰‰Š‹Œ‹Š‰‰‰Š‰ˆ‡‡‡‡ˆ‰ˆ†‡‡‡†‡‡‡‡‡‡‡‡‡‡ˆ††‡††††††‡‡†………………„„ƒƒƒƒ„„„ƒƒƒ„„ƒƒƒƒƒ‚‚‚„‚‚‚‚‚ƒƒ‚‚‚…ƒ€‚‚‚ƒ„ƒ‚ƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„………†‡‡‡‡‡‡†…†‡††‡‡‡‡‡‡‡‡‡…‡‡ˆ‹Š‡…†‡‡‰‰‰‰ˆˆˆˆ‹Œ‹Šˆˆ‡ˆ‡††‡‡‡‡ˆ‡††‡†…†‡‡‡‡‡‡‡‡‡‡†††‡‡‡†††††††††……†…„„ƒ„„„„ƒ‚ƒƒ‚‚‚ƒƒƒƒƒƒƒ‚‚‚„ƒƒ„ƒ„ƒ‚ƒƒƒƒ„„„ƒƒƒƒƒ„„„ƒƒƒ„………††††††‡†……†‡†‡‡††‡‡‡‡‡†††‰‰‡‡‡‡‡‡‡‡‡ˆ‡ˆ‰‰ŠŠ‹Šˆˆ‡‡‡ˆˆˆˆ‡‡‡††…‡‡†‡‡‡‡‡‡††‡‡‡‡††‡‡‡†††††††…††……„ƒƒ„„…„ƒ‚‚ƒƒ„ƒ‚ƒƒƒƒƒ‚€ƒ„…„‚‚‚‚‚€‚ƒƒ‚‚ƒ„ƒ‚„ƒƒ‚‚ƒƒƒ„„„ƒƒƒƒƒ„…††††…†‡‡‡‡‡‡‡‡†††‡‡‡‡‡†††ˆˆ……†‡‡ˆ‰ˆ‡‡ˆ‰Š‰‹ŠŠŒ‹ŠŠŠ‰‰‰ˆˆ‡ˆˆ‡‡‡‡‡‡‡‡†††……†‡‡‡‡†‡‡‡‡‡‡†††………††††…„ƒƒ„ƒ„ƒƒ‚ƒƒ‚‚‚‚‚‚ƒƒ‚‚ƒ…„‚‚‚€‚‚‚‚ƒ‚‚‚‚‚ƒƒƒƒƒ‚ƒ„ƒƒ„„„„„„„„„…†‡†††††††††………………††‡‡‡†‡ˆ‡††…†‡‡ˆˆˆ‡ˆ‰ŠŠˆ‰Š‹Š‹‹ŠŠ‰‰Šˆˆˆ‰ˆ‡‡‡‡†…†…†‡†…†‡‡‡‡‡†‡‡‡‡†††……………††…„„„„„„ƒ‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚~€€~~~~~€‚‚‚‚‚‚‚‚‚ƒƒ„„„„„„„ƒ„„„………††††‡……††…„„…†††……†‡‡‡††ˆ†…„…‡‡ˆˆ‰ˆ‡ˆ‰Š‹ˆŠŠŠŠ‹‰‰‰ŠŠŠŠ‰‰ˆˆ‡‡‡††‡‡††‡‡†…†‡‡‡‡‡‡†‡††…„„„„……†…„„„„…„ƒƒƒ‚‚‚‚‚‚‚€‚…††…‚~~€‚ƒ‚ƒ…„€‚‚‚‚ƒ„ƒ„„„„„……„…………„„……„„…††„„…††††††‡ˆ‡††ˆ†„„†‡‡‡ˆˆ‡‡ˆ‰‰‰ˆ‹Š‰Š‹ŠŠ‰Š‹Š‹Š‰‰‰ˆ‡‡†…†‡†‰ˆ‡†…†‡‡‡‡‡††††……„„„„„„ƒ„„„„„„ƒƒƒ‚‚‚‚‚‚‚‚‚€€„…†ˆˆ‡†ƒ„„…†…ƒ€€‚‚‚‚‚ƒ‚‚ƒƒƒ„„„„…………„„„…†††………†††††††‡‡‡‡††‡‡……††‡‡‡††‡‰‰‰‰ˆŒŒ‹Š‰‰Š‰‰Š‹‹Šˆˆˆˆ†††……†…†‡‡††‡†††………„„„„„„„„„„„„„ƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€€‚„‡‡„€~}}~‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒ„ƒƒƒƒƒƒ„„„„„„………„…………†††…‡‡‡‡ˆ‡†††…†‡‡‡‡†‡‡ˆˆ‡ˆ‰‡‹‰ˆ‡ˆ‰‰Š‰ˆŠ‹‹Š‰‰ˆ‡†………†‡‡‡‡†††……„ƒ„„„„„„ƒ„„„„„„„…„ƒ‚‚ƒƒƒ‚‚‚‚€€€€€€€€€€€€€€‚ƒ†‰ˆ…ƒ„…‚€€‚‚‚ƒƒƒƒ„„ƒƒƒ„„„„„„„ƒ„†…†…„…††‡…†‡‡‡‡ˆˆ‡……†††‡ˆ‡‡‡ˆˆˆˆ‰ˆ†‰‡‡ˆˆˆ‰‰‰‰‰‰ˆ‰ˆ‡‡‡†„ƒ‚…††‡‡…„„„……ƒƒƒƒƒ„„„……„„ƒƒ„„ƒ‚‚‚ƒ‚‚‚‚‚‚€‚‚€€€€€€€‚„†‡‚€€€€€‚‚‚ƒ‚‚ƒƒƒƒ„„„„„„„…††………†††††††ˆŠˆ†…†‡‡‡‡‡‡‡ˆˆˆˆ‰Šˆ‡‡†‡‰‰‰ŠŠŠ‰‰ŠŠŠ‰‡††……„‚‚…‡ˆ‡…„„„„…„„ƒƒ‚ƒƒƒ„„„ƒƒ„…„ƒ‚‚‚‚‚‚‚€€‚‚€€€€€€€€€€€€€€€€€€‚‚‚‚ƒƒƒƒ„„„……„ƒ„…………††††‡‡†‡ˆˆ†„…†‡‡‡‡‡‡‡‡ˆˆˆˆˆ‡‡ˆˆ‡‰ŒŠ‰Š‹ŠŠŠŠ‰‡‡ˆ‡†…„‚ƒ†‡‡†…………„„ƒƒ„ƒ‚‚ƒ‚ƒƒ„ƒƒƒƒ„ƒ‚‚‚‚‚‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒƒƒ‚‚ƒƒƒƒƒ„„ƒƒ„„…„„„„…††‡‡…‡ˆ†„„‡‡††‡‡‡ˆˆ‡‡‡ˆ‡†‡ˆˆˆˆˆ‹‹Š‹Œ‹‰‰‰ˆ‡‡‡ˆ†„ƒ‚‚†ˆ‡†„…………ƒƒ„„ƒƒƒƒ‚‚‚ƒƒ‚‚‚ƒ‚‚‚ƒ‚‚€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€~€€€€‚‚‚€‚ƒƒƒƒ„„ƒƒ„…………„„„†……††‡‡…ƒ†‡‡††‡‡‡‡‡‡‡ˆˆ‡‡‡ˆˆ‰‰‰‰Š‹‹Œ‹‰‰ˆ‡‡‡‡‡‡†„‚ƒ‡‡†„„„„ƒ‚‚ƒ„„ƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚ƒƒ‚|}€}}}€€€€€€€€€€€~~~€€€€€€€€‚ƒƒƒƒƒ„„„„…††…„…†††††‡ˆ„…††††††‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‰‹‹‹‹ŠŠ‰‰ˆ‡‡†‡‡‡‡†††ˆ‡…„ƒƒƒƒƒ‚ƒ„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚~€ƒ‚€€€~|{|€€|{|{{|}|}}~~||}||{{z{|€||}|{||~€€‚‚ƒƒ„„„…„„„…„„„………††‡‡„……………†††††‡ˆˆˆˆ‡‡‡‡‰ˆˆˆ‰Š‹‰ˆ‰ˆ‡ˆˆ‡‡††††………†‡†……„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€ƒ€~}††}~~}}}|{||}€€€~€€~~€‚}~€}{}€€‚‚‚ƒƒƒƒƒ„ƒƒƒ„„„……………††…………†……†††††††‡‡ˆ‡‡ˆˆŠ‰‹Šˆ‰‹‰ˆ‰Š‡‡‡‡‡‡††‡†ƒ„……†…„„„„ƒƒ„„ƒ‚‚‚‚‚€€€‚}{~€ƒ‡…}€ƒ‚‚€€~‚„„„„„‚~€„€€€‚……‚€|}€€€€‚‚‚‚‚ƒ„„ƒƒ„„„…„………………†††††††…„„††…††‡ˆˆ‡‡‡ˆ‘ŒŠ‰‰‹Š‰Š‰‡‡‡†…†‡††…‚ƒˆ†„„……„„„„ƒƒƒƒƒ€€‚€€}}|~€„…‚€~………‡†‡„€€€}}‚ƒ‚ƒ„ƒ€ƒ€~}‚„‡ˆ‚€{~€€€€€‚‚‚‚ƒƒƒ„„ƒ„„……†††………„………†‡‡†…†††‡‡ˆ‰ˆ‡‡†‡•‘‹‰‰‰‰‰‰‡‡‡‡†„…‡††„€€€„Ї………„ƒƒƒƒƒ„ƒƒƒ€€€€€€€|~|{€…„€€€€€€€€€€€€€~}~€€€|z~€€‡…€}|€€€€‚‚‚‚‚ƒ„„ƒƒƒƒ„…†††††…†‡‡‡‡‡‡‡†…††††‡ˆ‰ˆˆ‡ˆ”’‘ŽŠ‰Š‰‰‰ˆˆ‡‡†„ƒƒ‚€€€€€…Ї…„„„„„„„„„ƒ‚‚‚€€€€€€€€€|}~~~„ƒ€€€€~~~}~~€€€~}|~~~€‚€|y~€€‚„~}€€€‚‚‚‚ƒ„ƒƒ‚ƒƒƒ„…†………†††‡‡‡††ˆ‡‡†††‡ˆ‡‡ˆˆ‡‡‡“ŽŠˆ‹‰ˆ‰‰‡††††„€€€€€…ˆ†††„‚ƒ„„„ƒƒ‚‚‚‚€€€€€€€€€{|~€~~~€~|}~}|{{{||~~||{{zy{|{~‚€|{~€€€~€€€€€€€‚‚ƒƒ„„„ƒƒ‚ƒƒ„…††††‡‡‡ˆˆ†††††‡‡‡‡‰Šˆ‡ˆˆˆˆˆ‘Œ‰‹ˆ‡ˆ‡†…†…„~€…ˆ†††…ƒ‚ƒ‚‚‚ƒ‚‚ƒƒ‚€€€€€€€€€€€~}~ƒ}~~~~~~€€€~}}|{~€~~}}}}~z~‚‚||€€€€€€‚ƒ„ƒ‚ƒ‚ƒ„„„„………†‡‡‡‡‡‡†††……‡‡‡‡ˆ‰ˆ‡‡ˆ‰‰‹‘’’‘ŽŠŠ‰ˆ‰ˆ‡†…‚‚€€€€€ƒˆ‡…„ƒƒƒƒ‚‚‚‚‚ƒƒ‚‚€€€€€€€€~~~ƒ€~}~~~}…†……„ƒ}}€‚‚‚‚‚€€}}||}€‚€€€€€ƒƒ‚„ƒƒƒ„„„………†‡‡ˆˆ‡‡†……††‡‡ˆˆˆ‡‡‡‡ˆ‡Š‘‘’‘ŽŒŒ‰ˆ‡‡†…ƒ€€€€€€ƒˆˆ†……ƒƒ„ƒ‚€€‚‚€€‚€€€€€€€€~}~€‚}~~~~~}€‚‚‚~~ƒ„„„„€€€€‚‚‚‚‚ƒƒƒƒƒ„„…‡†ˆˆˆ‡†„„„…‡‡‰‰ˆˆ‡‡‡‡‡ˆ‹Œ‘‘‰‡…ƒ€€€€€€€‚ƒˆˆ†††„„ƒ‚€€€€€€€€€€€€€€~}}€~~}~~}}~~}|}~~~€‚~‚„„‚€€€€€‚‚‚‚‚‚ƒƒƒƒ…„…†‡‡ˆˆˆ‡†„……†‡‡ˆˆ‡‡‡††ˆ‰ŠŒ’‘‰„€€€€€€€€€€€†ˆ†……„„ƒ‚€‚ƒ‚‚‚‚€‚‚€€€€€€€€}~€€€~~~~||||~~€€~}}~~~~€€€‚~|‚€€€€€€€€€‚‚ƒƒ‚‚‚ƒƒƒƒ…„„…†‡‡‡ˆ‡ˆ†‡‡†…†‡‡ˆ‰‹Š‰ˆ‡††ˆŠŒŒŒŠ‡ƒ‚€€€€€€€€€„ˆ…ƒ„„ƒƒƒƒ‚‚‚‚ƒƒƒ‚€€€€€~~€~€~~}€~~~}{{{{||}~€~~||}~~~~~ƒ{|€€€€€€€€€‚ƒ„„…ƒ‚ƒ„……„„…„„…††‡‡†‡Šˆ†……„…‡‡‡ˆŠŠ‰ˆˆˆ‡ˆŠŒ‹‘‘Šˆ†‚€€€€€€€€€€€€„‰„ƒƒƒ‚ƒ‚‚‚€€‚ƒ‚‚€€~€€‚‚~~€€}~~~~~~}}}}|{z}€€~}{{||{zz}‚}|}€€€€€€€‚ƒ„„„„ƒ‚ƒ„„„…„„……††‡‡‡†ˆ‡…„ƒ„†‡‡‡ˆ‰ˆˆˆ‡‡ˆŠ‹‹‰ˆŽˆ†…„€€~€€€€€€€€€„ˆ…„ƒƒ‚‚‚‚‚‚‚€€€~€€€€€‚€€€€€€~~€€~}z}‚~€€€€€€€‚ƒ…„‚‚‚ƒ„„………†‡‡‡‡‡†‡ˆ†…………†‡‡‡ˆˆ‡‡‡ˆˆŠŒŒŠˆ‡‹‰‡…„………€€€€€€€€€€€€‚‡†„ƒƒƒ‚ƒ‚€€€~}€€€~€€€€€€€‚ƒƒ‚‚‚‚€€€‚‚‚‚~}€ƒ‚€€€€€‚ƒ„„„ƒƒƒƒ„„„………†‡‡‡‡‡‡‡†„„†‡‡‡‡‡‡††††‡‡ˆŠ‰‡‡‡ˆ……†…„„„ƒ€€€€€€€€€€€€‚ˆ…‚‚‚‚‚‚€‚€€€€~~~~~€€€€€€€€€€€€€€€~~}}~€€ƒ‚‚‚ƒ„………ƒƒ„……„„†‡‡‡‡‡†……„…‡‡ˆˆ††……†‡‡‰Š‹ˆ†‡‡ƒ…†††…„ƒ€€€€€€€€€€€€‡‡ƒ‚ƒ€€€€€€~~€€€~€€~~~~~~~~~~~~~~~~~}}}}~~~~~~~}~~€€€‚ƒ€€‚ƒƒ„…„……‡†……††‡‡‡‡…†††‡ˆˆˆ†††…†‡ˆ‰‰‰ˆˆˆ‡ƒ„……ƒ‚‚€€€€€~€€€€€€…ˆ…‚‚‚‚‚€€€~€€€€€€~}|}~€~~}}~~~~}~~}}~~~~}}}~~~~~~~~€€€€€‚‚‚ƒ‚ƒ…„„…‡‡††††††‡†…††††‡ˆˆ‡……††‡ˆ‹Š‰ˆˆ‡†ƒƒ‚‚ƒƒ‚€€€‚€€€€€€€€„ˆ†ƒ‚‚€€€€€€€€€€€€€€€€~|}}||}|}~~}~~~~~~~~}}}~~~~~~}}~~~~}~~€€€€}~‚€€€€€€‚ƒƒƒ…………………†††††ˆ†………„…‡ˆˆ‡††††ˆ‰‹‰ˆ‡†‡ˆ…ƒ‚€‚„ƒ€€€€€€€€€€€ƒ‡…‚‚‚€€€€€€€€€€€€€€€€€€€|{}}~~~}{}~~~~~}~}{{||}}}}}|||||}}}}}}}}~}|{{€€€|{z€€€€€€€€€‚ƒƒƒƒ„……„„……†‡††‡ˆ†„…‡„…‡††‡‡††‡‰‹‹ˆˆˆ‡‡‡ƒƒƒ‚‚‚‚€€€€€€€€€€€€~€†‡‚‚‚€€€€€€€€€€€~|~€€ƒ„‚}}|}~~~~~}|||{|}~}}|}}|{{}}}}}}}}}|z€€‚€|{€€€€€‚ƒ„‚‚„„„„…††‡†‡‡‰…„††††††††………‡ˆ‰‰ˆˆˆˆ‡ˆ„ƒƒ€€€€€€€€€€€€„‡ƒ‚‚€€€€€€~{}‚„ƒ€€€~~~~~}|}}{{}}}}}|}}{{}}|}}}|}€}}{€€||€€€‚ƒƒƒ‚ƒƒ„„„…„…†‡‰†‚ƒ…†††‡‡†……‡‡‡‡†‡‡‡ˆˆˆ‡€€€€€€€€€€€~€€€€€…„„€€€€€€€€€€€€}|~†~~~~}~~~}}}||}~~}|}}}}|{|}}|}}}~~}|}€~~|€€€{}~~~€ƒ‚‚„ƒ„„„„„ƒ„†ˆ‡€ƒƒ‚„…†††……†‡‡ˆˆ†††‡ˆˆˆ‡€€€€€€€€€€€€€€€€€€€€††…ƒ€€€€€€€€€€€€€€€€€||ƒ‚~}}~~~~~~~}}}|}{{}€~}~}}}}}}~||}~~~}}€~}{~€~~{}€€€€€‚ƒ„„„„„„…ˆ‹‚~€‚‚ƒ†‡‡††…†‡‡‡ˆ†‡‡‡‡‡‡‡€€€€€€€€€€€€€€€€€€€„‡„‚€€€€€€€€€||‚€~~}~~~~}}}}||||}}~€~~}|}}}~~}~~{||}~~}}~~}}~~‚|}€€€€€€€€€€€€‚„ƒƒƒ‚‚„‰‡‚ƒ‡†……………‡‡‡‡‡ˆˆ‡‡‡‡ˆ€€‚€€€€€€€€€€€€€€€€€€‚†ƒ€€€€€€€€€~€€~~~€~~~~~~}}}}}}|{|}€~}}~}}}}|}|}|{|~~€~~~~~~ƒ€||€€€€€€€€€€€€€‚‚€‚ƒ…Š‚€€‚……ƒƒ„„†††‡‡‡‡‡‡ˆˆˆˆ‰€€€€€€€€€€€€€€€€€€€€„„ƒ‚€€€€€€€€}€~~~~~~~}}}~}|||€~~}}}|}|||}{||~{{{}}~}€€~~~}{}z|€€€€€€€€€€€€ƒ‰†€€ƒƒ‚ƒ„††††‡‡ˆ‰ˆ‡‡‡‡‰‰€€€€€‚€€€€€€€€€€€€„†„‚€€€€€€€~~~~~~|~~~}}~}}}}}}||~€}||||||}}}|~~}{|||}~~~}{}‚~{|€€|y}~€€€€€€€€€€€€€ƒƒƒ‡‰€€€ƒƒ„…†‡……„…‡‡‡‡‡†‡‡‡ˆ€€€€€€€€€€€€€€~€€€€„„ƒ‚€€€€€~~~}~~~}}~}~~||}|{}}}|}~~~}{||}~|z{{|}|}}|||||}~€}|||€‚~|z~‚~z}~~€€€€€€€€€€€‚‚…Šƒ€€€€€€€ƒ„…††…„„„‡‡‡‡‡ˆˆ‰ˆ‡€€€€€€€€€€€€€€€€€„ƒƒƒ€€€€€~~~~~~}}~~}{z{|||}z{}~}}~~}|{z|{||||~~{{|~~}{}~‚z{‚{|~€€€€€€€€€€€‚‰‡€€€€~~ƒ„…†††…†‡‡‡‡‡‡‡‡ˆˆ€€‚€€€€€€€€€€€€€€€€†„„„‚€€€€€€€~~}~}}}~~~€||}~~~~}|}}||~}~~}|||||}y|}{|~€ƒ{|€€€€€€€€€€ƒ†‰‚€€€€€€€€ƒ…ƒ„…†‡††††‡ˆ‡‡ˆ‰‰€€€€‚€€€€€€€€€€€€€€€€„ƒ‚€€€€€€€€~~~~~}~€€€}|}~}||~~}}}}}|||~~~~|||}{{€~{x{}}~~~‚~{|€€€€€€€€€€€ƒ‰„~€€€€€€„„ƒ„…†…†‡……‡ˆ‡‡ˆ‰€€€€€€€€‚€€€€€€€€…€‚„€€€€~~~~~€€€}}~~~~}||{|||{|~}{}}|}}|}}}}{z~{y{|}}}~}~~zz~€€~~€€€€€‚…‰€€€€€€€‚ƒ„„…†‡‡…†ˆ‰Š‰ŠŒ€€€€€€€€€€~~€€€€€€€†€ƒ„‚€€€€€€~~~~~~~}}}}}}}~~~~||}}}}|{{}||||{|~~}~~}}}}}}}}zz~}||||}}}}}€||~€€€€‚…‡‚€€€€€€‚„„…†‡ˆ‡ˆ‰‰ŠŠ‹Œ|~€€€~€€€€€€€€€€€‚ˆ~„„‚€€€€€~~~}~~}}}}}}}}}}}}}}|{|{z|{z|}~~}}}~~~}}~~||}}}}||}}~~~~~€€€€‚„„‡‚€€€€€€€€~ƒƒ„…††‡‡ˆŠŽŽŽqrruy{~€€€€€€€€€€‚‰ƒ€ƒ„ƒ‚€€€€€~~~~~~~}}}}||}~}}}~}}}}{{z{{{{||||||||}}|||||||||||}}}~~~~~~~~~~~~€€€€„…ƒ‡€€€€€€€€€€€~‚„„†‡‡‡‰ŒŒŽŽŽŽmkkmnnv€€€€€€€€€€€€ƒˆ…‚‚‚€€€€€€~~~~~~~~~~~~}}}}}}}||||}}}||{{||||}|||||{{{{{{|}}}}||}}}}}}}}~~~~~~~}~€€€€€€€€‚…‚‚ˆ€€€€€€€€ƒ†‡‡‰‰‰Œ‘‘kjjkkjr€€€€~€€€€€€€€€€€€‚†ƒ‚‚‚‚€€€€€~~~~~~~~}}|}}|}}}|}{{|}}||||{{{{|||||}}|||}|||}}|||}}~~~~~}}~~}~€€€€€€€„‚€ƒˆ‚€€€€€€€€€€€€€„†ˆˆˆŠŒ‘’’kjhhigr€€€€€€€€€€€€€ƒ†ƒ‚‚‚‚ƒ€€€€€~~~~~~~~~}~}}}|}}}|{||{|}}||||{{{{{|||||}}||}||||||||{|}~}}}}}~~~€€€€€€€„„‚‰‚€€€€€€€€€€€€€€€€€ƒ…‡‡ˆ‹‘’hhhhhdp~€€€€€€€†‡€€‚€€€€€~~}~~~}}}|}|||||||}||}}{z|||||{||}|{||{{{||{||}}}|{|||}}}~~~~~€€€€‚„‚ƒ„‰‚€€€€€€€€€€€€€€ƒ„†ˆ‰ŒŒ‘fffefbp€€€€€€€€€€€€€†ˆ€ƒ€€€€~~~~~~~}}}}}}||||||||||{|~}}{z~}}{z~}{{{{z|}}~}}|}}}}}}~~~~~~~~€€€€€€‚ƒƒ‚„‰‚€€€€€€€‚„†ˆ‰‹ŒŒŽffedd`p~€~€€€€€€€‡‰€€€‚‚‚€€€€€~~~~}~~~~}}}~}}|||{{||{{|||}}~~~}~}z}{z|}~}z|}|||||}}}}|}}}~~~€€€€ƒ„„ƒƒ‡ƒ€€€€€€€€€€~€€‚†‡‡ˆ‰‹ŒŽeeeecbr€€€~~€€€€€€€€€€ˆˆ€€€‚€€€€€€~~~}~~}||}}}}|||}}z|~}~~|}}~}~}{||y|||~xz|{||}}}}~~}~~~~~€€€€€€€€€‚ƒ‚‚‚‚‚†„€€€€€€€€€€€€ƒ††‡ˆ‰‹ŽŽccccb`q~~€€€€€~€€€ˆ‡€€€€€€€€€€€~~~~||}}}}|||~|{}}}}}|{{~|{{{{|y{|}~x|}}||}}}}~~~~€€€€~€‚‚‚ƒ‡„€€€€€€€€€€‚„„‡‡ˆŠŽŽŽŽedbaa_p€€€€€€€€€€€€€€€ˆ‡€€€€€€€€‚€€~~~}}~~~~~~}}}}}|{{{{||}}|z{}}|}}}}}}{{|{|}|zz~~}|{}~}}}~~~~~~}}~€‚‚€ƒ‡†€€€€€€€€€~……†‡‡ŠŒŽŽdddcb_q€€€€€€€€€€€€€€~€€Šˆ€€€€€ƒ‚€€€~||}}~~}}}}||{|}}||{|~}{{}}|||{|}|{{}|{{z{~~{{|||||||}}}~~~~€~~~ƒ‚€€‚ƒ‰‡€€€~~~~€€€…†…‡‡Š‹ŒŽcccca`p€€€€€€€€€€€‰‡€€€€€€€€€€€€€€€}}}}}|}}|}}|{|}}||}}}}|||}|}}{{{|~}}}~~~~}{|}}}}||||}}~~~~~‚„ƒ€‚‚ˆ‡~~~ƒ†‡‡‡ˆŠ‹ŒŽcbbccan€€€~~€€€€Š‡€€€€€€€€€€€€€€€€~}}}||||}}|{{{{||{{}~}|||{{}}}}}}||}~~}}|z{}}|||{{|}}~~~~€ƒ‚‚€€€‡‡€€~~‚†‡‡‡‰‹Ždddcbal€€~€€€‚€€€‚Š…€€€€€€€€€€€~~}}~~}}}||||{{{{||{{{||{{{zz||||{{{|||{{|{{{{{|}}}}}}~~€~~~~€‚‚€€€€‡‡€€€€€€€€~ƒ†‡‡ˆ‰‹ŒŽ’cccbbaj~€€€€€€€€€~‹„€€€€€€€~~~}~~~}|}}||||}}|||||||{{{{{|{{zzzz{{|{{{{{{{{|}}}~~~~~~~~~~~€€€€€€€€‡‡€€€€€€€€€€€~€„†‡†ˆ‰‹Œ•dcccdcj~€€€€€€€€€€€Š…€€€€€€€€€€€~~~}}||{|}}}}}|}~~~~~}}}}|||{z{{{{}}}~}}}}||||}}}~~~~~}}~~~€‚€€€€€‚†‡€€€€€€ƒ‡‡†‰ˆˆŒ“•ddeeecg|€~€€€€€€€€€€€ˆ„€€€€€€€€€€€€€€~~~~}}||||}}}}{{||{{|z{}~}|}|{|}~~}zz||z{|~~}}}}}~~}~~~~}~~ƒ„€€€€€ƒ€€‡†€€€€€€€€€€†††‰‰ŠŒ’–eeeffdf{€€€€€€€€€€€€€€‰„€€€€€€€€€€€€~~~~}}|||}}|}|zzzzz{{}~}}|{|~}z}}|||{{z{|~~~|}~~~~~~|||}~€€‚„‚€€€ƒ€‡‡€€€€€€€€€€ƒƒ‚…†‡‡ˆ‹Ž‘•fghfhfgz€€€€€€€€€€€€€€Š…€€€€€€€€€€€€€€€€~~~~~}}}||||||}}|{zzz{{{|~}|{~}z{}{zz{{{|}z|~}}}}}}~~}|}~~€ƒƒ€€€~‚‡‡~€€€€€€€ƒ„‚ƒ„…†ˆ‹“efffgegx€€€€€€€€€€~‚‹…‚‚€€€€€€€€€€€€€~~~~}~}|}}}{{||~~}}}{|~|}}||}{||z{}~~}}|~}z|}}}~~~~}}}}~€€€€€€€€ƒˆ†€€€€€€€€€€€€„…„†‡‡ŠŽ‘effhjjiv~€€€€€€€€€€‚‹„‚€€‚€€€€€€€€€€€~~~}}}}|}}}{{||~}|||||}|||||||{{{|}}||}~}}{|~~~~~~~~~}}~€€‚ƒ€€€€~€ƒ€€ƒŠ„~€€€€€€€~€€‚„……†ˆˆŠ‘ijkjkkks~€€€€~€€€‰…‚€€€€€€€€€€€€€€€€~~~~}}}}{{}}~|{{||}|}{{|}~z||{{|}}}~~}|{}|}}~~~~€‚€€~‚ƒ€‰ƒ~€‚€ƒ‚„†ˆ‰‹jjjjloms}€~€€€€€€€€€€‰…‚‚€€€€€~€€€€€€€~~}}}~|y}~~|||||}|}|{~||||}}}|||}}~~}|{|{|}}}~~€€€‚€€€€€‚ƒ‰ƒ€€€€€€€€€€€€€€‚„…‡‡Šmlmklmnu~€€€€€€€€€€€Š„‚€€€€€€€€€€€€€€€€€€~}}~~~~|{}|}}}}}|}}~|{}z{{|}|||||||}€~|z}~}}}}}~€€€€€€€€„€‚Šƒ€€€€€€€€€€€€€€…†‡‡ˆŽnmoonoqu}€€€€€€€€€€€‚‰…‚€€€€€€€€€€€€€€€€€€}}~~|}~}}}}}}}}}~|{}{|{{|{{||||}~€|zz}}}}}}~~€€€€€€€€€€€€„€€„Œƒ€€€€€€~€€‚‚ƒ„ƒ…†‰noopqpps}€€€€€€€€€€€€€…‡„‚€€€€€€~€€€€€€~~~~{|}~}}}|}||{|~{{||}||||}}~~|{z}}}}~~€€€€€€€€€€€€‚„€„‹‚€€€‚‚ƒƒ‚ƒ†npnnqrrs|€€€€€€€€€€€€€€ˆ„‚‚€€€€€€€€€€€€~~~~}||}~}}}~}|||~}{z||}}~}}}€||z|}|}~~€€€€€€€€€€~€€„ƒ€„‰€€€€€€~€€€‚ƒ…†oonprstu{€€€€€€€€…Šƒƒƒ€€€€€€€€€€~}~}}}€€~|}|}~}|z{{}~}}‚€|zy|}}~~~€€€€€€€~‚…‚€€…ˆ‚€€€€€€€€~€€€‚…†qrqrssuvz€€€€€€€€€€€†ˆƒ‚€€€€€€€€~€~~~}~}}~~~|}€~~}|}}|{~~~}{z||~€€€€~|y|}}}~~~€‚€€€‚„€€€…‡…€€€€€€€€€€€€ƒ‚‚‚‚rsrsuvvwy€€€€€€€€€€‚‰…€ƒ‚‚€€€‚‚€€~}€~~~{}~|z{|}}|{|}}}}|{|~}||||||{x{}|~~~~€€€‚€~€€„ƒ€„‡‰‚€€€€€€€€€‚ƒ‚‚‚€rrsuwuvwy€€€€€„Š„ƒƒƒ‚€€‚€€€€€€€~~€€~~€€~}}{{{{{{zz{z|~}||}}}~|{|}}}}|zx{~}|~~~€€€€~}}€‚„„…ˆƒ€€€€€€€€€€€€ƒ„‚‚ƒ‚‚‚stuvxwwwy~€€€€€€€€‰‹ƒ‚„„ƒ‚‚‚€€€€€€€€€€€~~}}~~~~~~~||{||{{{}~}}}}~}}}~~}|{yyxxy}~~~~~€€‚€€€€€~~€€ƒƒ‚‚‚„„††€€€€€€€~€€€‚€ƒrtuwxxxxy}€~€€€€€€€€‚‹ˆ‚‚„„ƒƒ‚‚‚€€€‚€€€€€€€‚~~||}}}}~~~~~€~~}}~}}}}~~~|}}}}}}~€€€€€„ƒ€€€€~€€…ƒ‚ƒ„„…‰€€€€€€€€€‚‚„ƒ€ƒtuwwxxyz{|€€€€€€€€€€€€„‹…ƒ„„„ƒƒ‚€€€‚€€€€€ƒ€€}}|}}}}~~~~~~~}~~~~~}}~~~~}~~~}~~}}}~€€€€ƒ‚€‚‚€€€€€€€€€‚…€ƒ„ƒ„‰„€€€€€€ƒƒ‚ƒƒƒ‚€wwxyyyz{{}€€€€€€€€€€€€‡‹†„„„„ƒ‚‚€‚‚€€€€~~€€€€€€~}||}}}}}~~}}}}~~}}~~}~}}}}|||||~~€€‚„‚~€€€€€€‚„€€€ƒ„ƒ„††€€€€€€€€€€€‚ƒ‚‚‚‚ƒ‚€‚‚‚wxxyy{~€€€€€€€€€€€‹‹†„‚ƒ…„ƒ‚‚‚‚€€€‚€€~~‚ƒ€€€}~€~}}}|||}}|}~~}}~~~~}}}|}}}||}}~~€€‚„‚‚„~~~}z~€€€€‚„‚€€‚„ƒƒ…‰€€€€€€€€€€€‚„‚‚‚‚ƒƒxz{|~~€€€€€€€€€€ƒŒ‰…„‚„†…„‚‚ƒ‚€€‚€€}}„ƒ€€}}}€€~~}|~}}}}}}}}}}|||}€‚ƒƒ‚€€‚|…„|}}|x}€€€€€ƒ†ƒ€‚ƒ…ƒ‚……Š„€€€€€€€€€€€€‚‚ƒƒƒ‚ƒ„ƒƒƒ{~€€€€€€€€€€€€€€…Œ‡……ƒƒ„„ƒƒ‚‚‚‚€}}€„„€€~}~€~||~~~~~~}‚€~}}~~~€‚ƒ~|ƒ†€|~€}|€€€„„‚€€‚‚‚…ƒ‚„…‰…€€€€€€€€€€ƒƒ‚‚‚‚‚‚„ƒƒ‚€€€€€ƒ‚€€€€€‚ˆ‹††…ƒ‚ƒƒƒƒƒƒ‚€‚‚‚~}…‚}|~€€~~€~~~€€€~€ƒ€|zyxxx{~~|€ƒ‚|}…‚~~€€€€€€€‚…ƒ€€‚‚ƒ…‚ƒ……‡ˆ€€€€€€‚€€ƒƒƒƒƒƒ‚ƒ„ƒƒ„ƒ‚€€€€€€€€‚‚Š‹††…„‚„„„ƒƒƒƒ€€€€€~~€~ƒ…€|}€€€~€€€~ƒ€|xwy|~}}{{z|€ƒ€|€‚}~€~€€€‚„„‚€‚‚ƒ„…‚ƒ……†‰„€€€€€€€…ƒ‚„„ƒƒƒƒ„„‚„„ƒ€‚‚‚‚‚‚€€€€‚€€€€‚‹‰†††„‚ƒ„„„ƒƒƒƒ‚€€€€€€€€~‚}†}~€€€€€}|{}}€~ƒ€}yy~€€€€{{|{|}€€€€€€€‚‚ƒ…‚€€‚ƒƒ„„‚ƒ„…†‡…€€€€€ƒ„†„‚„„ƒƒƒ„„ƒ‚‚‚ƒ‚„……‚€€€€€€€€€€…Œ‡‡‡†„‚ƒ„„„ƒƒƒ‚‚€€€€€€~‚|‚€}|~‚‚€€€||{}}~€€€€‚|zz€€€€€€€‚{|€~{|€€€€€€€€€€‚‚„„€‚‚‚ƒ„ƒƒ„„…†‡ˆ€€€€€ƒ€€‚„††‚‚„ƒ‚ƒ‚‚‚ƒ„ƒ„„ƒ„‚€€€€€€ˆŒ†††…„‚ƒ†……„„„„‚€‚€€€€‚‚}}~||‚‚€€€‚||~~}€€€€€€|}{€€€€ƒ|}‚~{~€€€€ƒ„ƒ‚€‚‚‚ƒ…„ƒ„„…„†Šƒ€€€€‚ƒ€„…†„ƒ…ƒ‚ƒ‚‚ƒ‚‚ƒƒ„…………„ƒ€€€€€€€€€Š‹†………„„…††…„„„„ƒ‚€€€}€€~|}‚‚€€€€}}}€|~~‚€€~}}€€€€‚„„ƒƒ|~ƒ|€€€€‚‚€€‚…„€‚…†„ƒ„„„…†‰‡€€‚‚‚„†…„‚„…ƒ‚‚‚ƒ„…‚……„ƒ‚„†„„€~€€‚„‹Š†††……ƒƒ…†…„„„…„ƒ‚‚‚‚‚‚~€}ƒ‚€€~}~‚‚}|}ƒ‚~|‚€€€€€€€~{~€|{‚}€€‡ƒ‚‚ƒ…‚€€€‚ƒ‚‚…†„‚ƒ„………‡ˆ‚€€‚ƒ„„ƒ……„…„ƒƒƒƒ‚‚ƒƒƒ„‚„„…†……„‚‚€€€€€€€„Љ‡‡‡‡†„„„„„„„„„ƒ‚‚‚‚~~ƒ|€|„‚€€~~…„„…|}€‚‚€~~€‚€€€{|~}{€}z~}~„„„‡†y|ƒ„ƒ‚‚ƒ„ƒƒ…†ƒƒ„„……†‡ˆ†€€„…„ƒƒƒƒƒ…‡ƒƒ‚‚‚‚ƒƒƒ…„……„…††…„„ƒ~€€€€‡‰‰‡‡‡‡†ƒ„………„„„„„‚€‚‚€|z‚€}€{~~~~~€€~~‚‚‚€~~€~~€~xx|}}}€ƒ~z}{{ƒ„ƒ€}y~€„…ƒ‚ƒ‚‚ƒ…‡…ƒƒ„„…†‡‡ˆ‡€€ƒ„ƒ‚ƒƒ…„‚‚‚ƒƒ†††††‡‡‡†…‡†€€€€€‚ˆˆ‡‡‡‡††„„††…„ƒ„„ƒƒ€‚€€~z~€€~|~€€~}}|{{}}}}€€€}}~€ƒ€~~~€„€‚ƒ~|z{}~~|z€‚„„ƒ€‚‚‚„††……„„„…†…†ˆˆ„€‚„ƒ‚‚„ƒ‚‚„„‚‚ƒ‚‚„„††………†††‡†††€€€€€€†ˆ‡ˆ‡‡‡‡‡†„„„………„„„ƒƒ‚‚€€€€€|~‚ƒ‚€€~}~|{{zzz}~€€~|}~„ƒ€……~|}~€€|{{{yz„…„‚‚‚‚ƒ„††…„„„„„………†‰‰€€‚ƒ‚‚ƒ„…ƒ…†ƒ„ƒ„……ƒƒ„†††…„†††††‡†€€„Œ‡‡‡‡‡‡†††„ƒ„…„„„„„ƒƒ‚‚€€‚€z{€ƒ|~~€€~€ƒ|z}‚ƒƒ„‚€~{}‚‚~}‚‚‚~{{|€€ƒ…„‚€‚ƒƒƒ„„†ˆ…„ƒƒ…†‡‡…†‰Šƒƒ‚„„………‡……††††„‚ƒ‡†ˆˆ‡‡‰ˆ††‡‡ƒ~€€€‡Š‡†‡‡‡††††„‚…††…………ƒƒ‚‚€‚‚€‚€}}€}yy{€€€€€|{|~}{}~~€€{{€‚ƒzz‚‚‚€€‚ƒ„„ƒ‚‚ƒƒƒ‚ƒ„…‡†„„………‡‡‡††ˆ‰‚€€‚ƒƒ„………………‡ˆ†ƒ‚ƒ†‡‰‰ˆ‡‡‡‡‡‡ˆ†€€€‚‰‡‡‡‡ˆ‡‡‡‡‡…ƒ…‡†††††…„ƒ‚‚‚ƒ„ƒƒ‚‚‚€€€€€€€€€€€€€|zz{}€€€~{||}}~}{|€|}€€€€‚‚ƒ……„ƒ‚ƒƒ„ƒƒ…††…ƒƒ„††‡‡‡‡‡‡‰‡…„„„ƒ‚ƒ…………„ƒ……ƒ‚ƒ„‡…†‡†„„†‡‡†‡‡ƒ€€‚ƒ†Š‡ˆ‡‡‡††‡‡‡…‚„†††††††……„ƒƒƒ„„ƒ‚‚€€€€€€€€€€€€€€€€€€€~|yyz{~‚€‚‚€€€‚‚„†…„ƒ‚‚„„„„…†‡†…„„„„†‡‡‡‡‡ˆˆˆ†„‚ƒ……ƒ‚€ƒ…††…ƒ„†‚ƒ„ˆ‡†‡ˆ‡‡ˆˆˆ‡‡ˆ„€ƒŠ‰‡‡‡ˆˆˆˆˆ‡‡†„ƒ…†‡…………………„‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€‚‚€€€€€€€€€€€€€€€€€€€‚ƒ‚‚‚‚€‚ƒ‚‚„„„ƒƒ‚‚ƒ„„„ƒ…†ˆ†ƒ„…………††‡‡†‡ˆˆ‡ƒ‚‚†…ƒƒ‚ƒ‡‡…†„ƒ…‚€„‡‡‡‡‡‡ˆˆˆ‰ˆˆˆƒ‚‡Œˆ‡ˆ‡‡‰Š‰‡‡ˆ†„„…†††……†………„‚ƒƒ‚ƒƒƒ‚‚‚‚‚ƒƒ‚‚‚‚‚‚€~€€‚‚‚‚‚€‚‚‚‚‚‚‚‚‚ƒƒƒƒ„……„ƒ‚‚ƒ„„„…†‡‡…„…†…†‡‡‡ˆˆ‡‡‡ˆˆƒ‚†…ƒ‚ƒƒƒ„……ƒƒƒ‚ƒƒ…ˆ‡ˆ‡†‡‡‰ˆ‡††…„€‚‹‰‡‰‡‡‰‰‰‡‡‰‡„ƒ…‡‡†††…†††…„„ƒƒƒ‚ƒƒ‚‚‚‚‚‚€‚‚€€€€€€‚‚‚‚‚‚‚‚ƒƒƒƒƒ‚‚‚ƒ‚‚‚‚‚‚‚ƒ„…„„„ƒ‚‚ƒ‚ƒƒ…††‡†„„„…††‡††††††‡ˆ‰†„ƒƒ…ƒ‚„„„…†„„ƒƒ‡…„‰‡‡‡†††ˆˆ‡‡‡‡†€~‡Š‡‡ˆ‡‰‹Šˆˆˆ‰ˆ…ƒ…††‡††††††……ƒƒ‚‚‚ƒƒ‚‚‚ƒƒ‚‚€€€€€€€€€‚‚‚ƒ‚‚ƒƒ‚‚‚„„ƒ„ƒƒ‚‚‚‚€€‚ƒƒƒ„††„„„ƒƒ„ƒƒ„…††‡…„…††††††…††††‡ˆŠ‡„†††…ƒƒ„†…„…………„…„ƒŠˆ‡††‡‡‡……‡‡‡‡…‚ŠŒŠ‡ˆ‰ˆˆŠ‰‡‡‰‰ˆ†„„……‡†††‡††††„ƒ„„ƒƒ‚‚ƒƒƒƒ‚‚‚‚‚€€€ƒƒ‚‚‚ƒ‚ƒƒ‚‚‚‚ƒƒ‚€€€ƒ„…„††„ƒ„„‚‚„„„„…††‡‡„„…„…††‡ˆ‡†††‡‡‡‰‰ƒ‡†…„„††††……………………
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 Binary files differnew file mode 100644 index 0000000000..01673bf6d5 --- /dev/null +++ b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 diff --git a/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp new file mode 100644 index 0000000000..8e013517fb --- /dev/null +++ b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/jpegdecoder.h> +#include <gtest/gtest.h> +#include <utils/Log.h> + +#include <fcntl.h> + +namespace android::recoverymap { + +#define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg" +#define YUV_IMAGE_SIZE 20193 +#define GREY_IMAGE "/sdcard/Documents/minnie-320x240-y.jpg" +#define GREY_IMAGE_SIZE 20193 + +class JpegDecoderTest : public testing::Test { +public: + struct Image { + std::unique_ptr<uint8_t[]> buffer; + size_t size; + }; + JpegDecoderTest(); + ~JpegDecoderTest(); +protected: + virtual void SetUp(); + virtual void TearDown(); + + Image mYuvImage, mGreyImage; +}; + +JpegDecoderTest::JpegDecoderTest() {} + +JpegDecoderTest::~JpegDecoderTest() {} + +static size_t getFileSize(int fd) { + struct stat st; + if (fstat(fd, &st) < 0) { + ALOGW("%s : fstat failed", __func__); + return 0; + } + return st.st_size; // bytes +} + +static bool loadFile(const char filename[], JpegDecoderTest::Image* result) { + int fd = open(filename, O_CLOEXEC); + if (fd < 0) { + return false; + } + int length = getFileSize(fd); + if (length == 0) { + close(fd); + return false; + } + result->buffer.reset(new uint8_t[length]); + if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) { + close(fd); + return false; + } + close(fd); + return true; +} + +void JpegDecoderTest::SetUp() { + if (!loadFile(YUV_IMAGE, &mYuvImage)) { + FAIL() << "Load file " << YUV_IMAGE << " failed"; + } + mYuvImage.size = YUV_IMAGE_SIZE; + if (!loadFile(GREY_IMAGE, &mGreyImage)) { + FAIL() << "Load file " << GREY_IMAGE << " failed"; + } + mGreyImage.size = GREY_IMAGE_SIZE; +} + +void JpegDecoderTest::TearDown() {} + +TEST_F(JpegDecoderTest, decodeYuvImage) { + JpegDecoder decoder; + EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size)); + ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0)); +} + +TEST_F(JpegDecoderTest, decodeGreyImage) { + JpegDecoder decoder; + EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size)); + ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0)); +} + +}
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/jpegencoder_test.cpp b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp new file mode 100644 index 0000000000..4cd2a5ef8c --- /dev/null +++ b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/jpegencoder.h> +#include <gtest/gtest.h> +#include <utils/Log.h> + +#include <fcntl.h> + +namespace android::recoverymap { + +#define VALID_IMAGE "/sdcard/Documents/minnie-320x240.yu12" +#define VALID_IMAGE_WIDTH 320 +#define VALID_IMAGE_HEIGHT 240 +#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y" +#define SINGLE_CHANNEL_IMAGE_WIDTH VALID_IMAGE_WIDTH +#define SINGLE_CHANNEL_IMAGE_HEIGHT VALID_IMAGE_HEIGHT +#define INVALID_SIZE_IMAGE "/sdcard/Documents/minnie-318x240.yu12" +#define INVALID_SIZE_IMAGE_WIDTH 318 +#define INVALID_SIZE_IMAGE_HEIGHT 240 +#define JPEG_QUALITY 90 + +class JpegEncoderTest : public testing::Test { +public: + struct Image { + std::unique_ptr<uint8_t[]> buffer; + size_t width; + size_t height; + }; + JpegEncoderTest(); + ~JpegEncoderTest(); +protected: + virtual void SetUp(); + virtual void TearDown(); + + Image mValidImage, mInvalidSizeImage, mSingleChannelImage; +}; + +JpegEncoderTest::JpegEncoderTest() {} + +JpegEncoderTest::~JpegEncoderTest() {} + +static size_t getFileSize(int fd) { + struct stat st; + if (fstat(fd, &st) < 0) { + ALOGW("%s : fstat failed", __func__); + return 0; + } + return st.st_size; // bytes +} + +static bool loadFile(const char filename[], JpegEncoderTest::Image* result) { + int fd = open(filename, O_CLOEXEC); + if (fd < 0) { + return false; + } + int length = getFileSize(fd); + if (length == 0) { + close(fd); + return false; + } + result->buffer.reset(new uint8_t[length]); + if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) { + close(fd); + return false; + } + close(fd); + return true; +} + +void JpegEncoderTest::SetUp() { + if (!loadFile(VALID_IMAGE, &mValidImage)) { + FAIL() << "Load file " << VALID_IMAGE << " failed"; + } + mValidImage.width = VALID_IMAGE_WIDTH; + mValidImage.height = VALID_IMAGE_HEIGHT; + if (!loadFile(INVALID_SIZE_IMAGE, &mInvalidSizeImage)) { + FAIL() << "Load file " << INVALID_SIZE_IMAGE << " failed"; + } + mInvalidSizeImage.width = INVALID_SIZE_IMAGE_WIDTH; + mInvalidSizeImage.height = INVALID_SIZE_IMAGE_HEIGHT; + if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) { + FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed"; + } + mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH; + mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT; +} + +void JpegEncoderTest::TearDown() {} + +TEST_F(JpegEncoderTest, validImage) { + JpegEncoder encoder; + EXPECT_TRUE(encoder.compressImage(mValidImage.buffer.get(), mValidImage.width, + mValidImage.height, JPEG_QUALITY, NULL, 0)); + ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); +} + +TEST_F(JpegEncoderTest, invalidSizeImage) { + JpegEncoder encoder; + EXPECT_FALSE(encoder.compressImage(mInvalidSizeImage.buffer.get(), mInvalidSizeImage.width, + mInvalidSizeImage.height, JPEG_QUALITY, NULL, 0)); +} + +TEST_F(JpegEncoderTest, singleChannelImage) { + JpegEncoder encoder; + EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width, + mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true)); + ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); +} + +} + diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp new file mode 100644 index 0000000000..0f96723189 --- /dev/null +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/recoverymap.h> +#include <fcntl.h> +#include <fstream> +#include <gtest/gtest.h> +#include <utils/Log.h> + +#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010" +#define RAW_P010_IMAGE_WIDTH 1280 +#define RAW_P010_IMAGE_HEIGHT 720 +#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg" + +#define SAVE_ENCODING_RESULT true +#define SAVE_DECODING_RESULT true + +namespace android::recoverymap { + +class RecoveryMapTest : public testing::Test { +public: + RecoveryMapTest(); + ~RecoveryMapTest(); +protected: + virtual void SetUp(); + virtual void TearDown(); + + struct jpegr_uncompressed_struct mRawP010Image; + struct jpegr_compressed_struct mJpegImage; +}; + +RecoveryMapTest::RecoveryMapTest() {} +RecoveryMapTest::~RecoveryMapTest() {} + +void RecoveryMapTest::SetUp() {} +void RecoveryMapTest::TearDown() { + free(mRawP010Image.data); + free(mJpegImage.data); +} + +static size_t getFileSize(int fd) { + struct stat st; + if (fstat(fd, &st) < 0) { + ALOGW("%s : fstat failed", __func__); + return 0; + } + return st.st_size; // bytes +} + +static bool loadFile(const char filename[], void*& result, int* fileLength) { + int fd = open(filename, O_CLOEXEC); + if (fd < 0) { + return false; + } + int length = getFileSize(fd); + if (length == 0) { + close(fd); + return false; + } + if (fileLength != nullptr) { + *fileLength = length; + } + result = malloc(length); + if (read(fd, result, length) != static_cast<ssize_t>(length)) { + close(fd); + return false; + } + close(fd); + return true; +} + +TEST_F(RecoveryMapTest, build) { + // Force all of the recovery map lib to be linked by calling all public functions. + RecoveryMap recovery_map; + recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr); + recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), + nullptr, 0, nullptr); + recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0), + nullptr); + recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr); + recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); +} + +TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { + int ret; + + // Load input files. + if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { + FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; + } + mRawP010Image.width = RAW_P010_IMAGE_WIDTH; + mRawP010Image.height = RAW_P010_IMAGE_HEIGHT; + mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; + + RecoveryMap recoveryMap; + + jpegr_compressed_struct jpegR; + jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t); + jpegR.data = malloc(jpegR.maxLength); + ret = recoveryMap.encodeJPEGR( + &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_ENCODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)jpegR.data, jpegR.length); + } + + jpegr_uncompressed_struct decodedJpegR; + int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4; + decodedJpegR.data = malloc(decodedJpegRSize); + ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_DECODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); + } + + free(jpegR.data); + free(decodedJpegR.data); +} + +TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) { + int ret; + + // Load input files. + if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { + FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; + } + mRawP010Image.width = RAW_P010_IMAGE_WIDTH; + mRawP010Image.height = RAW_P010_IMAGE_HEIGHT; + mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; + + if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) { + FAIL() << "Load file " << JPEG_IMAGE << " failed"; + } + mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709; + + RecoveryMap recoveryMap; + + jpegr_compressed_struct jpegR; + jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t); + jpegR.data = malloc(jpegR.maxLength); + ret = recoveryMap.encodeJPEGR( + &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_ENCODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)jpegR.data, jpegR.length); + } + + jpegr_uncompressed_struct decodedJpegR; + int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4; + decodedJpegR.data = malloc(decodedJpegRSize); + ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_DECODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); + } + + free(jpegR.data); + free(decodedJpegR.data); +} + +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp new file mode 100644 index 0000000000..169201c73b --- /dev/null +++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp @@ -0,0 +1,882 @@ +/* + * Copyright 2022 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 <cmath> +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <jpegrecoverymap/recoverymapmath.h> + +namespace android::recoverymap { + +class RecoveryMapMathTest : public testing::Test { +public: + RecoveryMapMathTest(); + ~RecoveryMapMathTest(); + + float ComparisonEpsilon() { return 1e-4f; } + float LuminanceEpsilon() { return 1e-2f; } + + Color Yuv420(uint8_t y, uint8_t u, uint8_t v) { + return {{{ static_cast<float>(y) / 255.0f, + (static_cast<float>(u) - 128.0f) / 255.0f, + (static_cast<float>(v) - 128.0f) / 255.0f }}}; + } + + Color P010(uint16_t y, uint16_t u, uint16_t v) { + return {{{ static_cast<float>(y) / 940.0f, + (static_cast<float>(u) - 64.0f) / 940.0f - 0.5f, + (static_cast<float>(v) - 64.0f) / 940.0f - 0.5f }}}; + } + + float Map(uint8_t e) { + return (static_cast<float>(e) - 127.5f) / 127.5f; + } + + Color ColorMin(Color e1, Color e2) { + return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}}; + } + + Color ColorMax(Color e1, Color e2) { + return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}}; + } + + Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; } + Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; } + + Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; } + Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; } + Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; } + + Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; } + Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; } + + Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; } + Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; } + Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; } + + Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; } + Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; } + Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; } + + float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) { + Color rgb_gamma = srgbYuvToRgb(yuv_gamma); + Color rgb = srgbInvOetf(rgb_gamma); + float luminance_scaled = luminanceFn(rgb); + return luminance_scaled * kSdrWhiteNits; + } + + float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf, + ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn, + float scale_factor) { + Color rgb_gamma = bt2100YuvToRgb(yuv_gamma); + Color rgb = hdrInvOetf(rgb_gamma); + rgb = gamutConversionFn(rgb); + float luminance_scaled = luminanceFn(rgb); + return luminance_scaled * scale_factor; + } + + Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) { + Color rgb_gamma = srgbYuvToRgb(yuv_gamma); + Color rgb = srgbInvOetf(rgb_gamma); + return applyRecovery(rgb, recovery, range_scaling_factor); + } + + jpegr_uncompressed_struct Yuv420Image() { + static uint8_t pixels[] = { + // Y + 0x00, 0x10, 0x20, 0x30, + 0x01, 0x11, 0x21, 0x31, + 0x02, 0x12, 0x22, 0x32, + 0x03, 0x13, 0x23, 0x33, + // U + 0xA0, 0xA1, + 0xA2, 0xA3, + // V + 0xB0, 0xB1, + 0xB2, 0xB3, + }; + return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 }; + } + + Color (*Yuv420Colors())[4] { + static Color colors[4][4] = { + { + Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0), + Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1), + }, { + Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0), + Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1), + }, { + Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2), + Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3), + }, { + Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2), + Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3), + }, + }; + return colors; + } + + jpegr_uncompressed_struct P010Image() { + static uint16_t pixels[] = { + // Y + 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6, + 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6, + 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6, + 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6, + // UV + 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6, + 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6, + }; + return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 }; + } + + Color (*P010Colors())[4] { + static Color colors[4][4] = { + { + P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0), + P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1), + }, { + P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0), + P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1), + }, { + P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2), + P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3), + }, { + P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2), + P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3), + }, + }; + return colors; + } + + jpegr_uncompressed_struct MapImage() { + static uint8_t pixels[] = { + 0x00, 0x10, 0x20, 0x30, + 0x01, 0x11, 0x21, 0x31, + 0x02, 0x12, 0x22, 0x32, + 0x03, 0x13, 0x23, 0x33, + }; + return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED }; + } + + float (*MapValues())[4] { + static float values[4][4] = { + { + Map(0x00), Map(0x10), Map(0x20), Map(0x30), + }, { + Map(0x01), Map(0x11), Map(0x21), Map(0x31), + }, { + Map(0x02), Map(0x12), Map(0x22), Map(0x32), + }, { + Map(0x03), Map(0x13), Map(0x23), Map(0x33), + }, + }; + return values; + } + +protected: + virtual void SetUp(); + virtual void TearDown(); +}; + +RecoveryMapMathTest::RecoveryMapMathTest() {} +RecoveryMapMathTest::~RecoveryMapMathTest() {} + +void RecoveryMapMathTest::SetUp() {} +void RecoveryMapMathTest::TearDown() {} + +#define EXPECT_RGB_EQ(e1, e2) \ + EXPECT_FLOAT_EQ((e1).r, (e2).r); \ + EXPECT_FLOAT_EQ((e1).g, (e2).g); \ + EXPECT_FLOAT_EQ((e1).b, (e2).b) + +#define EXPECT_RGB_NEAR(e1, e2) \ + EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \ + EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \ + EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon()) + +#define EXPECT_RGB_CLOSE(e1, e2) \ + EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \ + EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \ + EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f) + +#define EXPECT_YUV_EQ(e1, e2) \ + EXPECT_FLOAT_EQ((e1).y, (e2).y); \ + EXPECT_FLOAT_EQ((e1).u, (e2).u); \ + EXPECT_FLOAT_EQ((e1).v, (e2).v) + +#define EXPECT_YUV_NEAR(e1, e2) \ + EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \ + EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \ + EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon()) + +#define EXPECT_YUV_BETWEEN(e, min, max) \ + EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \ + EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \ + EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v))) + +// TODO: a bunch of these tests can be parameterized. + +TEST_F(RecoveryMapMathTest, ColorConstruct) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + EXPECT_FLOAT_EQ(e1.r, 0.1f); + EXPECT_FLOAT_EQ(e1.g, 0.2f); + EXPECT_FLOAT_EQ(e1.b, 0.3f); + + EXPECT_FLOAT_EQ(e1.y, 0.1f); + EXPECT_FLOAT_EQ(e1.u, 0.2f); + EXPECT_FLOAT_EQ(e1.v, 0.3f); +} + +TEST_F(RecoveryMapMathTest, ColorAddColor) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 + e1; + EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f); + + e2 += e1; + EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f); +} + +TEST_F(RecoveryMapMathTest, ColorAddFloat) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 + 0.1f; + EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f); + EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f); + EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f); + + e2 += 0.1f; + EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f); + EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f); + EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f); +} + +TEST_F(RecoveryMapMathTest, ColorSubtractColor) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 - e1; + EXPECT_FLOAT_EQ(e2.r, 0.0f); + EXPECT_FLOAT_EQ(e2.g, 0.0f); + EXPECT_FLOAT_EQ(e2.b, 0.0f); + + e2 -= e1; + EXPECT_FLOAT_EQ(e2.r, -e1.r); + EXPECT_FLOAT_EQ(e2.g, -e1.g); + EXPECT_FLOAT_EQ(e2.b, -e1.b); +} + +TEST_F(RecoveryMapMathTest, ColorSubtractFloat) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 - 0.1f; + EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f); + EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f); + EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f); + + e2 -= 0.1f; + EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f); + EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f); + EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f); +} + +TEST_F(RecoveryMapMathTest, ColorMultiplyFloat) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 * 2.0f; + EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f); + + e2 *= 2.0f; + EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f); +} + +TEST_F(RecoveryMapMathTest, ColorDivideFloat) { + Color e1 = {{{ 0.1f, 0.2f, 0.3f }}}; + + Color e2 = e1 / 2.0f; + EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f); + + e2 /= 2.0f; + EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f); + EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f); + EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f); +} + +TEST_F(RecoveryMapMathTest, SrgbLuminance) { + EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f); + EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f); + EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f); + EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f); + EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f); +} + +TEST_F(RecoveryMapMathTest, SrgbYuvToRgb) { + Color rgb_black = srgbYuvToRgb(YuvBlack()); + EXPECT_RGB_NEAR(rgb_black, RgbBlack()); + + Color rgb_white = srgbYuvToRgb(YuvWhite()); + EXPECT_RGB_NEAR(rgb_white, RgbWhite()); + + Color rgb_r = srgbYuvToRgb(SrgbYuvRed()); + EXPECT_RGB_NEAR(rgb_r, RgbRed()); + + Color rgb_g = srgbYuvToRgb(SrgbYuvGreen()); + EXPECT_RGB_NEAR(rgb_g, RgbGreen()); + + Color rgb_b = srgbYuvToRgb(SrgbYuvBlue()); + EXPECT_RGB_NEAR(rgb_b, RgbBlue()); +} + +TEST_F(RecoveryMapMathTest, SrgbRgbToYuv) { + Color yuv_black = srgbRgbToYuv(RgbBlack()); + EXPECT_YUV_NEAR(yuv_black, YuvBlack()); + + Color yuv_white = srgbRgbToYuv(RgbWhite()); + EXPECT_YUV_NEAR(yuv_white, YuvWhite()); + + Color yuv_r = srgbRgbToYuv(RgbRed()); + EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed()); + + Color yuv_g = srgbRgbToYuv(RgbGreen()); + EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen()); + + Color yuv_b = srgbRgbToYuv(RgbBlue()); + EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue()); +} + +TEST_F(RecoveryMapMathTest, SrgbRgbYuvRoundtrip) { + Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack())); + EXPECT_RGB_NEAR(rgb_black, RgbBlack()); + + Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite())); + EXPECT_RGB_NEAR(rgb_white, RgbWhite()); + + Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed())); + EXPECT_RGB_NEAR(rgb_r, RgbRed()); + + Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen())); + EXPECT_RGB_NEAR(rgb_g, RgbGreen()); + + Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue())); + EXPECT_RGB_NEAR(rgb_b, RgbBlue()); +} + +TEST_F(RecoveryMapMathTest, SrgbTransferFunction) { + EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f); + EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon()); + EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon()); + EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f); +} + +TEST_F(RecoveryMapMathTest, P3Luminance) { + EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f); + EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f); + EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f); + EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f); + EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f); +} + +TEST_F(RecoveryMapMathTest, Bt2100Luminance) { + EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f); + EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f); + EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f); + EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f); + EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f); +} + +TEST_F(RecoveryMapMathTest, Bt2100YuvToRgb) { + Color rgb_black = bt2100YuvToRgb(YuvBlack()); + EXPECT_RGB_NEAR(rgb_black, RgbBlack()); + + Color rgb_white = bt2100YuvToRgb(YuvWhite()); + EXPECT_RGB_NEAR(rgb_white, RgbWhite()); + + Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed()); + EXPECT_RGB_NEAR(rgb_r, RgbRed()); + + Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen()); + EXPECT_RGB_NEAR(rgb_g, RgbGreen()); + + Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue()); + EXPECT_RGB_NEAR(rgb_b, RgbBlue()); +} + +TEST_F(RecoveryMapMathTest, Bt2100RgbToYuv) { + Color yuv_black = bt2100RgbToYuv(RgbBlack()); + EXPECT_YUV_NEAR(yuv_black, YuvBlack()); + + Color yuv_white = bt2100RgbToYuv(RgbWhite()); + EXPECT_YUV_NEAR(yuv_white, YuvWhite()); + + Color yuv_r = bt2100RgbToYuv(RgbRed()); + EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed()); + + Color yuv_g = bt2100RgbToYuv(RgbGreen()); + EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen()); + + Color yuv_b = bt2100RgbToYuv(RgbBlue()); + EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue()); +} + +TEST_F(RecoveryMapMathTest, Bt2100RgbYuvRoundtrip) { + Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack())); + EXPECT_RGB_NEAR(rgb_black, RgbBlack()); + + Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite())); + EXPECT_RGB_NEAR(rgb_white, RgbWhite()); + + Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed())); + EXPECT_RGB_NEAR(rgb_r, RgbRed()); + + Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen())); + EXPECT_RGB_NEAR(rgb_g, RgbGreen()); + + Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue())); + EXPECT_RGB_NEAR(rgb_b, RgbBlue()); +} + +TEST_F(RecoveryMapMathTest, HlgOetf) { + EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f); + EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon()); + EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon()); + EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f); + + Color e = {{{ 0.04167f, 0.08333f, 0.5f }}}; + Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}}; + EXPECT_RGB_NEAR(hlgOetf(e), e_gamma); +} + +TEST_F(RecoveryMapMathTest, HlgInvOetf) { + EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f); + EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon()); + EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon()); + EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f); + + Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}}; + Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}}; + EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e); +} + +TEST_F(RecoveryMapMathTest, HlgTransferFunctionRoundtrip) { + EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f); + EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon()); + EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon()); + EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f); +} + +TEST_F(RecoveryMapMathTest, PqOetf) { + EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f); + EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon()); + EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon()); + EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f); + + Color e = {{{ 0.01f, 0.5f, 0.99f }}}; + Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}}; + EXPECT_RGB_NEAR(pqOetf(e), e_gamma); +} + +TEST_F(RecoveryMapMathTest, PqInvOetf) { + EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f); + EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon()); + EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon()); + EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f); + + Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}}; + Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}}; + EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e); +} + +TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) { + EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f); + EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon()); + EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon()); + EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon()); + EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f); +} + +TEST_F(RecoveryMapMathTest, ColorConversionLookup) { + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709), + identityConversion); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3), + p3ToBt709); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100), + bt2100ToBt709); + + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709), + bt709ToP3); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3), + identityConversion); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100), + bt2100ToP3); + + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709), + bt709ToBt2100); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3), + p3ToBt2100); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100), + identityConversion); + + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3), + nullptr); + EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100), + nullptr); +} + +TEST_F(RecoveryMapMathTest, EncodeRecovery) { + EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127); + EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127); + EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0); + EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0); + + EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127); + EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255); + EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255); + EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0); + EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0); + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191); + EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63); + + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255); + EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0); + EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191); + EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63); + + EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255); + EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0); + EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191); + EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63); +} + +TEST_F(RecoveryMapMathTest, ApplyRecovery) { + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack()); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack()); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack()); + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f); + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f); + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f); + + Color e = {{{ 0.0f, 0.5f, 1.0f }}}; + + EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f); + EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e); + EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f); +} + +TEST_F(RecoveryMapMathTest, GetYuv420Pixel) { + jpegr_uncompressed_struct image = Yuv420Image(); + Color (*colors)[4] = Yuv420Colors(); + + for (size_t y = 0; y < 4; ++y) { + for (size_t x = 0; x < 4; ++x) { + EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]); + } + } +} + +TEST_F(RecoveryMapMathTest, GetP010Pixel) { + jpegr_uncompressed_struct image = P010Image(); + Color (*colors)[4] = P010Colors(); + + for (size_t y = 0; y < 4; ++y) { + for (size_t x = 0; x < 4; ++x) { + EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]); + } + } +} + +TEST_F(RecoveryMapMathTest, SampleYuv420) { + jpegr_uncompressed_struct image = Yuv420Image(); + Color (*colors)[4] = Yuv420Colors(); + + static const size_t kMapScaleFactor = 2; + for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) { + for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) { + Color min = {{{ 1.0f, 1.0f, 1.0f }}}; + Color max = {{{ -1.0f, -1.0f, -1.0f }}}; + + for (size_t dy = 0; dy < kMapScaleFactor; ++dy) { + for (size_t dx = 0; dx < kMapScaleFactor; ++dx) { + Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx]; + min = ColorMin(min, e); + max = ColorMax(max, e); + } + } + + // Instead of reimplementing the sampling algorithm, confirm that the + // sample output is within the range of the min and max of the nearest + // points. + EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max); + } + } +} + +TEST_F(RecoveryMapMathTest, SampleP010) { + jpegr_uncompressed_struct image = P010Image(); + Color (*colors)[4] = P010Colors(); + + static const size_t kMapScaleFactor = 2; + for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) { + for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) { + Color min = {{{ 1.0f, 1.0f, 1.0f }}}; + Color max = {{{ -1.0f, -1.0f, -1.0f }}}; + + for (size_t dy = 0; dy < kMapScaleFactor; ++dy) { + for (size_t dx = 0; dx < kMapScaleFactor; ++dx) { + Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx]; + min = ColorMin(min, e); + max = ColorMax(max, e); + } + } + + // Instead of reimplementing the sampling algorithm, confirm that the + // sample output is within the range of the min and max of the nearest + // points. + EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max); + } + } +} + +TEST_F(RecoveryMapMathTest, SampleMap) { + jpegr_uncompressed_struct image = MapImage(); + float (*values)[4] = MapValues(); + + static const size_t kMapScaleFactor = 2; + for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) { + for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) { + size_t x_base = x / kMapScaleFactor; + size_t y_base = y / kMapScaleFactor; + + float min = 1.0f; + float max = -1.0f; + + min = fmin(min, values[y_base][x_base]); + max = fmax(max, values[y_base][x_base]); + if (y_base + 1 < 4) { + min = fmin(min, values[y_base + 1][x_base]); + max = fmax(max, values[y_base + 1][x_base]); + } + if (x_base + 1 < 4) { + min = fmin(min, values[y_base][x_base + 1]); + max = fmax(max, values[y_base][x_base + 1]); + } + if (y_base + 1 < 4 && x_base + 1 < 4) { + min = fmin(min, values[y_base + 1][x_base + 1]); + max = fmax(max, values[y_base + 1][x_base + 1]); + } + + // Instead of reimplementing the sampling algorithm, confirm that the + // sample output is within the range of the min and max of the nearest + // points. + EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y), + testing::AllOf(testing::Ge(min), testing::Le(max))); + } + } +} + +TEST_F(RecoveryMapMathTest, ColorToRgba1010102) { + EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30); + EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF); + EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff); + EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10); + EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20); + + Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}}; + EXPECT_EQ(colorToRgba1010102(e_gamma), + 0x3 << 30 + | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff)) + | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10 + | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20); +} + +TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) { + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance), + 0.0f); + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance), + kSdrWhiteNits); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance), + srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance), + srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance), + srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); +} + +TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbP3) { + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance), + 0.0f); + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance), + kSdrWhiteNits); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance), + p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance), + p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance), + p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); +} + +TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbBt2100) { + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance), + 0.0f); + EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance), + kSdrWhiteNits); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance), + bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance), + bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); + EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance), + bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); +} + +TEST_F(RecoveryMapMathTest, GenerateMapLuminanceHlg) { + EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion, + bt2100Luminance, kHlgMaxNits), + 0.0f); + EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion, + bt2100Luminance, kHlgMaxNits), + kHlgMaxNits); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion, + bt2100Luminance, kHlgMaxNits), + bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon()); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion, + bt2100Luminance, kHlgMaxNits), + bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon()); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion, + bt2100Luminance, kHlgMaxNits), + bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon()); +} + +TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) { + EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion, + bt2100Luminance, kPqMaxNits), + 0.0f); + EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion, + bt2100Luminance, kPqMaxNits), + kPqMaxNits); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion, + bt2100Luminance, kPqMaxNits), + bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon()); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion, + bt2100Luminance, kPqMaxNits), + bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon()); + EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion, + bt2100Luminance, kPqMaxNits), + bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon()); +} + +//Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) { +TEST_F(RecoveryMapMathTest, ApplyMap) { + EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f), + RgbWhite() * 8.0f); + EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f), + RgbBlack()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f), + RgbRed() * 8.0f); + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f), + RgbGreen() * 8.0f); + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f), + RgbBlue() * 8.0f); + + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f), + RgbWhite() * sqrt(8.0f)); + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f), + RgbBlack()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f), + RgbRed() * sqrt(8.0f)); + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f), + RgbGreen() * sqrt(8.0f)); + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f), + RgbBlue() * sqrt(8.0f)); + + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f), + RgbWhite()); + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f), + RgbBlack()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f), + RgbRed()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f), + RgbGreen()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f), + RgbBlue()); + + EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f), + RgbWhite() / sqrt(8.0f)); + EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f), + RgbBlack()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f), + RgbRed() / sqrt(8.0f)); + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f), + RgbGreen() / sqrt(8.0f)); + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f), + RgbBlue() / sqrt(8.0f)); + + EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f), + RgbWhite() / 8.0f); + EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f), + RgbBlack()); + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f), + RgbRed() / 8.0f); + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f), + RgbGreen() / 8.0f); + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f), + RgbBlue() / 8.0f); +} + +} // namespace android::recoverymap diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 8240b08085..e64165fef3 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -14,13 +14,10 @@ * limitations under the License. */ -#define LOG_TAG "Choreographer" -//#define LOG_NDEBUG 0 - #include <android-base/thread_annotations.h> -#include <gui/DisplayEventDispatcher.h> -#include <gui/ISurfaceComposer.h> +#include <android/gui/ISurfaceComposer.h> #include <jni.h> +#include <nativedisplay/Choreographer.h> #include <private/android/choreographer.h> #include <utils/Looper.h> #include <utils/Timers.h> @@ -31,444 +28,9 @@ #include <queue> #include <thread> -namespace { -struct { - // Global JVM that is provided by zygote - JavaVM* jvm = nullptr; - struct { - jclass clazz; - jmethodID getInstance; - jmethodID registerNativeChoreographerForRefreshRateCallbacks; - jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; - } displayManagerGlobal; -} gJni; - -// Gets the JNIEnv* for this thread, and performs one-off initialization if we -// have never retrieved a JNIEnv* pointer before. -JNIEnv* getJniEnv() { - if (gJni.jvm == nullptr) { - ALOGW("AChoreographer: No JVM provided!"); - return nullptr; - } - - JNIEnv* env = nullptr; - if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { - ALOGD("Attaching thread to JVM for AChoreographer"); - JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; - jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); - if (attachResult != JNI_OK) { - ALOGE("Unable to attach thread. Error: %d", attachResult); - return nullptr; - } - } - if (env == nullptr) { - ALOGW("AChoreographer: No JNI env available!"); - } - return env; -} - -inline const char* toString(bool value) { - return value ? "true" : "false"; -} -} // namespace - -namespace android { -using gui::VsyncEventData; - -struct FrameCallback { - AChoreographer_frameCallback callback; - AChoreographer_frameCallback64 callback64; - AChoreographer_vsyncCallback vsyncCallback; - void* data; - nsecs_t dueTime; - - inline bool operator<(const FrameCallback& rhs) const { - // Note that this is intentionally flipped because we want callbacks due sooner to be at - // the head of the queue - return dueTime > rhs.dueTime; - } -}; - -struct RefreshRateCallback { - AChoreographer_refreshRateCallback callback; - void* data; - bool firstCallbackFired = false; -}; - -class Choreographer; - -/** - * Implementation of AChoreographerFrameCallbackData. - */ -struct ChoreographerFrameCallbackDataImpl { - int64_t frameTimeNanos{0}; - - VsyncEventData vsyncEventData; - - const Choreographer* choreographer; -}; - -struct { - std::mutex lock; - std::vector<Choreographer*> ptrs GUARDED_BY(lock); - std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock); - bool registeredToDisplayManager GUARDED_BY(lock) = false; - - std::atomic<nsecs_t> mLastKnownVsync = -1; -} gChoreographers; - -class Choreographer : public DisplayEventDispatcher, public MessageHandler { -public: - explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock); - void postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, - AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay); - void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) - EXCLUDES(gChoreographers.lock); - void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); - // Drains the queue of pending vsync periods and dispatches refresh rate - // updates to callbacks. - // The assumption is that this method is only called on a single - // processing thread, either by looper or by AChoreographer_handleEvents - void handleRefreshRateUpdates(); - void scheduleLatestConfigRequest(); - - enum { - MSG_SCHEDULE_CALLBACKS = 0, - MSG_SCHEDULE_VSYNC = 1, - MSG_HANDLE_REFRESH_RATE_UPDATES = 2, - }; - virtual void handleMessage(const Message& message) override; - - static Choreographer* getForThread(); - virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); - int64_t getFrameInterval() const; - bool inCallback() const; - -private: - Choreographer(const Choreographer&) = delete; - - void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, - VsyncEventData vsyncEventData) override; - void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, - nsecs_t vsyncPeriod) override; - void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; - void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, - std::vector<FrameRateOverride> overrides) override; - - void scheduleCallbacks(); - - ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; - void registerStartTime() const; - - std::mutex mLock; - // Protected by mLock - std::priority_queue<FrameCallback> mFrameCallbacks; - std::vector<RefreshRateCallback> mRefreshRateCallbacks; - - nsecs_t mLatestVsyncPeriod = -1; - VsyncEventData mLastVsyncEventData; - bool mInCallback = false; - - const sp<Looper> mLooper; - const std::thread::id mThreadId; - - // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway. - static constexpr size_t kMaxStartTimes = 250; -}; - -static thread_local Choreographer* gChoreographer; -Choreographer* Choreographer::getForThread() { - if (gChoreographer == nullptr) { - sp<Looper> looper = Looper::getForThread(); - if (!looper.get()) { - ALOGW("No looper prepared for thread"); - return nullptr; - } - gChoreographer = new Choreographer(looper); - status_t result = gChoreographer->initialize(); - if (result != OK) { - ALOGW("Failed to initialize"); - return nullptr; - } - } - return gChoreographer; -} - -Choreographer::Choreographer(const sp<Looper>& looper) - : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp), - mLooper(looper), - mThreadId(std::this_thread::get_id()) { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.ptrs.push_back(this); -} - -Choreographer::~Choreographer() { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), - gChoreographers.ptrs.end(), - [=](Choreographer* c) { return c == this; }), - gChoreographers.ptrs.end()); - // Only poke DisplayManagerGlobal to unregister if we previously registered - // callbacks. - if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { - gChoreographers.registeredToDisplayManager = false; - JNIEnv* env = getJniEnv(); - if (env == nullptr) { - ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); - return; - } - jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, - gJni.displayManagerGlobal.getInstance); - if (dmg == nullptr) { - ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); - } else { - env->CallVoidMethod(dmg, - gJni.displayManagerGlobal - .unregisterNativeChoreographerForRefreshRateCallbacks); - env->DeleteLocalRef(dmg); - } - } -} - -void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, - AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; - { - std::lock_guard<std::mutex> _l{mLock}; - mFrameCallbacks.push(callback); - } - if (callback.dueTime <= now) { - if (std::this_thread::get_id() != mThreadId) { - if (mLooper != nullptr) { - Message m{MSG_SCHEDULE_VSYNC}; - mLooper->sendMessage(this, m); - } else { - scheduleVsync(); - } - } else { - scheduleVsync(); - } - } else { - if (mLooper != nullptr) { - Message m{MSG_SCHEDULE_CALLBACKS}; - mLooper->sendMessageDelayed(delay, this, m); - } else { - scheduleCallbacks(); - } - } -} - -void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { - std::lock_guard<std::mutex> _l{mLock}; - for (const auto& callback : mRefreshRateCallbacks) { - // Don't re-add callbacks. - if (cb == callback.callback && data == callback.data) { - return; - } - } - mRefreshRateCallbacks.emplace_back( - RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); - bool needsRegistration = false; - { - std::lock_guard<std::mutex> _l2(gChoreographers.lock); - needsRegistration = !gChoreographers.registeredToDisplayManager; - } - if (needsRegistration) { - JNIEnv* env = getJniEnv(); - if (env == nullptr) { - ALOGW("JNI environment is unavailable, skipping registration"); - return; - } - jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, - gJni.displayManagerGlobal.getInstance); - if (dmg == nullptr) { - ALOGW("DMS is not initialized yet: skipping registration"); - return; - } else { - env->CallVoidMethod(dmg, - gJni.displayManagerGlobal - .registerNativeChoreographerForRefreshRateCallbacks, - reinterpret_cast<int64_t>(this)); - env->DeleteLocalRef(dmg); - { - std::lock_guard<std::mutex> _l2(gChoreographers.lock); - gChoreographers.registeredToDisplayManager = true; - } - } - } else { - scheduleLatestConfigRequest(); - } -} - -void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, - void* data) { - std::lock_guard<std::mutex> _l{mLock}; - mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), - mRefreshRateCallbacks.end(), - [&](const RefreshRateCallback& callback) { - return cb == callback.callback && - data == callback.data; - }), - mRefreshRateCallbacks.end()); -} - -void Choreographer::scheduleLatestConfigRequest() { - if (mLooper != nullptr) { - Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; - mLooper->sendMessage(this, m); - } else { - // If the looper thread is detached from Choreographer, then refresh rate - // changes will be handled in AChoreographer_handlePendingEvents, so we - // need to wake up the looper thread by writing to the write-end of the - // socket the looper is listening on. - // Fortunately, these events are small so sending packets across the - // socket should be atomic across processes. - DisplayEventReceiver::Event event; - event.header = - DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, - PhysicalDisplayId::fromPort(0), systemTime()}; - injectEvent(event); - } -} - -void Choreographer::scheduleCallbacks() { - const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - nsecs_t dueTime; - { - std::lock_guard<std::mutex> _l{mLock}; - // If there are no pending callbacks then don't schedule a vsync - if (mFrameCallbacks.empty()) { - return; - } - dueTime = mFrameCallbacks.top().dueTime; - } - - if (dueTime <= now) { - ALOGV("choreographer %p ~ scheduling vsync", this); - scheduleVsync(); - return; - } -} - -void Choreographer::handleRefreshRateUpdates() { - std::vector<RefreshRateCallback> callbacks{}; - const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); - const nsecs_t lastPeriod = mLatestVsyncPeriod; - if (pendingPeriod > 0) { - mLatestVsyncPeriod = pendingPeriod; - } - { - std::lock_guard<std::mutex> _l{mLock}; - for (auto& cb : mRefreshRateCallbacks) { - callbacks.push_back(cb); - cb.firstCallbackFired = true; - } - } - - for (auto& cb : callbacks) { - if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { - cb.callback(pendingPeriod, cb.data); - } - } -} - -// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the -// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for -// the internal display implicitly. -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, - VsyncEventData vsyncEventData) { - std::vector<FrameCallback> callbacks{}; - { - std::lock_guard<std::mutex> _l{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { - callbacks.push_back(mFrameCallbacks.top()); - mFrameCallbacks.pop(); - } - } - mLastVsyncEventData = vsyncEventData; - for (const auto& cb : callbacks) { - if (cb.vsyncCallback != nullptr) { - const ChoreographerFrameCallbackDataImpl frameCallbackData = - createFrameCallbackData(timestamp); - registerStartTime(); - mInCallback = true; - cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( - &frameCallbackData), - cb.data); - mInCallback = false; - } else if (cb.callback64 != nullptr) { - cb.callback64(timestamp, cb.data); - } else if (cb.callback != nullptr) { - cb.callback(timestamp, cb.data); - } - } -} - -void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, - to_string(displayId).c_str(), toString(connected)); -} - -void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { - LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); -} - -void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId, - std::vector<FrameRateOverride>) { - LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered"); -} - -void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { - ALOGV("choreographer %p ~ received null event.", this); - handleRefreshRateUpdates(); -} - -void Choreographer::handleMessage(const Message& message) { - switch (message.what) { - case MSG_SCHEDULE_CALLBACKS: - scheduleCallbacks(); - break; - case MSG_SCHEDULE_VSYNC: - scheduleVsync(); - break; - case MSG_HANDLE_REFRESH_RATE_UPDATES: - handleRefreshRateUpdates(); - break; - } -} - -int64_t Choreographer::getFrameInterval() const { - return mLastVsyncEventData.frameInterval; -} - -bool Choreographer::inCallback() const { - return mInCallback; -} - -ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { - return {.frameTimeNanos = timestamp, - .vsyncEventData = mLastVsyncEventData, - .choreographer = this}; -} - -void Choreographer::registerStartTime() const { - std::scoped_lock _l(gChoreographers.lock); - for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) { - while (gChoreographers.startTimes.size() >= kMaxStartTimes) { - gChoreographers.startTimes.erase(gChoreographers.startTimes.begin()); - } - gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC); - } -} +#undef LOG_TAG +#define LOG_TAG "AChoreographer" -} // namespace android using namespace android; static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { @@ -488,27 +50,12 @@ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl( // Glue for private C api namespace android { -void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.mLastKnownVsync.store(vsyncPeriod); - for (auto c : gChoreographers.ptrs) { - c->scheduleLatestConfigRequest(); - } +void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) { + Choreographer::signalRefreshRateCallbacks(vsyncPeriod); } void AChoreographer_initJVM(JNIEnv* env) { - env->GetJavaVM(&gJni.jvm); - // Now we need to find the java classes. - jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); - gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass)); - gJni.displayManagerGlobal.getInstance = - env->GetStaticMethodID(dmgClass, "getInstance", - "()Landroid/hardware/display/DisplayManagerGlobal;"); - gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = - env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); - gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = - env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", - "()V"); + Choreographer::initJVM(env); } AChoreographer* AChoreographer_routeGetInstance() { @@ -583,13 +130,7 @@ int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) { } int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) { - std::scoped_lock _l(gChoreographers.lock); - const auto iter = gChoreographers.startTimes.find(vsyncId); - if (iter == gChoreographers.startTimes.end()) { - ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId); - return 0; - } - return iter->second; + return Choreographer::getStartTimeNanosForVsyncId(vsyncId); } } // namespace android diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index 76b85d6002..bf0805b46c 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -117,15 +117,6 @@ using namespace android::display::impl; #define CHECK_NOT_NULL(name) \ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); -namespace { - -sp<IBinder> getToken(ADisplay* display) { - DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); - return SurfaceComposerClient::getPhysicalDisplayToken(impl->id); -} - -} // namespace - namespace android { int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { @@ -136,19 +127,20 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { } std::vector<DisplayConfigImpl> modesPerDisplay[size]; + ui::DisplayConnectionType displayConnectionTypes[size]; int numModes = 0; for (int i = 0; i < size; ++i) { - const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]); - ui::StaticDisplayInfo staticInfo; - if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo); + if (const status_t status = + SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo); status != OK) { return status; } + displayConnectionTypes[i] = staticInfo.connectionType; ui::DynamicDisplayInfo dynamicInfo; if (const status_t status = - SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo); + SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo); status != OK) { return status; } @@ -168,8 +160,6 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { } } - const std::optional<PhysicalDisplayId> internalId = - SurfaceComposerClient::getInternalDisplayId(); ui::Dataspace defaultDataspace; ui::PixelFormat defaultPixelFormat; ui::Dataspace wcgDataspace; @@ -201,8 +191,9 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { for (size_t i = 0; i < size; ++i) { const PhysicalDisplayId id = ids[i]; - const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL - : ADisplayType::DISPLAY_TYPE_EXTERNAL; + const ADisplayType type = (displayConnectionTypes[i] == ui::DisplayConnectionType::Internal) + ? ADisplayType::DISPLAY_TYPE_INTERNAL + : ADisplayType::DISPLAY_TYPE_EXTERNAL; const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i]; memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size()); @@ -259,14 +250,15 @@ void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outData int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { CHECK_NOT_NULL(display); - sp<IBinder> token = getToken(display); ui::DynamicDisplayInfo info; - if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info); + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + + if (const auto status = + SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info); status != OK) { return status; } - DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); for (size_t i = 0; i < impl->numConfigs; i++) { auto* config = impl->configs + i; if (config->id == info.activeDisplayModeId) { diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 4659b96b11..70de33da12 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -53,8 +53,10 @@ cc_library_shared { version_script: "libnativedisplay.map.txt", srcs: [ + ":libgui_frame_event_aidl", "AChoreographer.cpp", "ADisplay.cpp", + "Choreographer.cpp", "surfacetexture/surface_texture.cpp", "surfacetexture/SurfaceTexture.cpp", "surfacetexture/ImageConsumer.cpp", diff --git a/libs/nativedisplay/Choreographer.cpp b/libs/nativedisplay/Choreographer.cpp new file mode 100644 index 0000000000..01e9f04d15 --- /dev/null +++ b/libs/nativedisplay/Choreographer.cpp @@ -0,0 +1,390 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 + +#include <jni.h> +#include <nativedisplay/Choreographer.h> + +#undef LOG_TAG +#define LOG_TAG "AChoreographer" + +namespace { +struct { + // Global JVM that is provided by zygote + JavaVM* jvm = nullptr; + struct { + jclass clazz; + jmethodID getInstance; + jmethodID registerNativeChoreographerForRefreshRateCallbacks; + jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; + } displayManagerGlobal; +} gJni; + +// Gets the JNIEnv* for this thread, and performs one-off initialization if we +// have never retrieved a JNIEnv* pointer before. +JNIEnv* getJniEnv() { + if (gJni.jvm == nullptr) { + ALOGW("AChoreographer: No JVM provided!"); + return nullptr; + } + + JNIEnv* env = nullptr; + if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + ALOGD("Attaching thread to JVM for AChoreographer"); + JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; + jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); + if (attachResult != JNI_OK) { + ALOGE("Unable to attach thread. Error: %d", attachResult); + return nullptr; + } + } + if (env == nullptr) { + ALOGW("AChoreographer: No JNI env available!"); + } + return env; +} + +inline const char* toString(bool value) { + return value ? "true" : "false"; +} +} // namespace + +namespace android { + +Choreographer::Context Choreographer::gChoreographers; + +static thread_local Choreographer* gChoreographer; + +void Choreographer::initJVM(JNIEnv* env) { + env->GetJavaVM(&gJni.jvm); + // Now we need to find the java classes. + jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); + gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass)); + gJni.displayManagerGlobal.getInstance = + env->GetStaticMethodID(dmgClass, "getInstance", + "()Landroid/hardware/display/DisplayManagerGlobal;"); + gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); + gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", + "()V"); +} + +Choreographer* Choreographer::getForThread() { + if (gChoreographer == nullptr) { + sp<Looper> looper = Looper::getForThread(); + if (!looper.get()) { + ALOGW("No looper prepared for thread"); + return nullptr; + } + gChoreographer = new Choreographer(looper); + status_t result = gChoreographer->initialize(); + if (result != OK) { + ALOGW("Failed to initialize"); + return nullptr; + } + } + return gChoreographer; +} + +Choreographer::Choreographer(const sp<Looper>& looper) + : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp), + mLooper(looper), + mThreadId(std::this_thread::get_id()) { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.ptrs.push_back(this); +} + +Choreographer::~Choreographer() { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), + gChoreographers.ptrs.end(), + [=](Choreographer* c) { return c == this; }), + gChoreographers.ptrs.end()); + // Only poke DisplayManagerGlobal to unregister if we previously registered + // callbacks. + if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { + gChoreographers.registeredToDisplayManager = false; + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .unregisterNativeChoreographerForRefreshRateCallbacks); + env->DeleteLocalRef(dmg); + } + } +} + +void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, + AChoreographer_vsyncCallback vsyncCallback, void* data, + nsecs_t delay) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; + { + std::lock_guard<std::mutex> _l{mLock}; + mFrameCallbacks.push(callback); + } + if (callback.dueTime <= now) { + if (std::this_thread::get_id() != mThreadId) { + if (mLooper != nullptr) { + Message m{MSG_SCHEDULE_VSYNC}; + mLooper->sendMessage(this, m); + } else { + scheduleVsync(); + } + } else { + scheduleVsync(); + } + } else { + if (mLooper != nullptr) { + Message m{MSG_SCHEDULE_CALLBACKS}; + mLooper->sendMessageDelayed(delay, this, m); + } else { + scheduleCallbacks(); + } + } +} + +void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { + std::lock_guard<std::mutex> _l{mLock}; + for (const auto& callback : mRefreshRateCallbacks) { + // Don't re-add callbacks. + if (cb == callback.callback && data == callback.data) { + return; + } + } + mRefreshRateCallbacks.emplace_back( + RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); + bool needsRegistration = false; + { + std::lock_guard<std::mutex> _l2(gChoreographers.lock); + needsRegistration = !gChoreographers.registeredToDisplayManager; + } + if (needsRegistration) { + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping registration"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet: skipping registration"); + return; + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .registerNativeChoreographerForRefreshRateCallbacks, + reinterpret_cast<int64_t>(this)); + env->DeleteLocalRef(dmg); + { + std::lock_guard<std::mutex> _l2(gChoreographers.lock); + gChoreographers.registeredToDisplayManager = true; + } + } + } else { + scheduleLatestConfigRequest(); + } +} + +void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, + void* data) { + std::lock_guard<std::mutex> _l{mLock}; + mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), + mRefreshRateCallbacks.end(), + [&](const RefreshRateCallback& callback) { + return cb == callback.callback && + data == callback.data; + }), + mRefreshRateCallbacks.end()); +} + +void Choreographer::scheduleLatestConfigRequest() { + if (mLooper != nullptr) { + Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; + mLooper->sendMessage(this, m); + } else { + // If the looper thread is detached from Choreographer, then refresh rate + // changes will be handled in AChoreographer_handlePendingEvents, so we + // need to wake up the looper thread by writing to the write-end of the + // socket the looper is listening on. + // Fortunately, these events are small so sending packets across the + // socket should be atomic across processes. + DisplayEventReceiver::Event event; + event.header = + DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, + PhysicalDisplayId::fromPort(0), systemTime()}; + injectEvent(event); + } +} + +void Choreographer::scheduleCallbacks() { + const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t dueTime; + { + std::lock_guard<std::mutex> _l{mLock}; + // If there are no pending callbacks then don't schedule a vsync + if (mFrameCallbacks.empty()) { + return; + } + dueTime = mFrameCallbacks.top().dueTime; + } + + if (dueTime <= now) { + ALOGV("choreographer %p ~ scheduling vsync", this); + scheduleVsync(); + return; + } +} + +void Choreographer::handleRefreshRateUpdates() { + std::vector<RefreshRateCallback> callbacks{}; + const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); + const nsecs_t lastPeriod = mLatestVsyncPeriod; + if (pendingPeriod > 0) { + mLatestVsyncPeriod = pendingPeriod; + } + { + std::lock_guard<std::mutex> _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { + cb.callback(pendingPeriod, cb.data); + } + } +} + +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { + std::vector<FrameCallback> callbacks{}; + { + std::lock_guard<std::mutex> _l{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { + callbacks.push_back(mFrameCallbacks.top()); + mFrameCallbacks.pop(); + } + } + mLastVsyncEventData = vsyncEventData; + for (const auto& cb : callbacks) { + if (cb.vsyncCallback != nullptr) { + const ChoreographerFrameCallbackDataImpl frameCallbackData = + createFrameCallbackData(timestamp); + registerStartTime(); + mInCallback = true; + cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( + &frameCallbackData), + cb.data); + mInCallback = false; + } else if (cb.callback64 != nullptr) { + cb.callback64(timestamp, cb.data); + } else if (cb.callback != nullptr) { + cb.callback(timestamp, cb.data); + } + } +} + +void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { + ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, + to_string(displayId).c_str(), toString(connected)); +} + +void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { + LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); +} + +void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId, + std::vector<FrameRateOverride>) { + LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered"); +} + +void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { + ALOGV("choreographer %p ~ received null event.", this); + handleRefreshRateUpdates(); +} + +void Choreographer::handleMessage(const Message& message) { + switch (message.what) { + case MSG_SCHEDULE_CALLBACKS: + scheduleCallbacks(); + break; + case MSG_SCHEDULE_VSYNC: + scheduleVsync(); + break; + case MSG_HANDLE_REFRESH_RATE_UPDATES: + handleRefreshRateUpdates(); + break; + } +} + +int64_t Choreographer::getFrameInterval() const { + return mLastVsyncEventData.frameInterval; +} + +bool Choreographer::inCallback() const { + return mInCallback; +} + +ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { + return {.frameTimeNanos = timestamp, + .vsyncEventData = mLastVsyncEventData, + .choreographer = this}; +} + +void Choreographer::registerStartTime() const { + std::scoped_lock _l(gChoreographers.lock); + for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) { + while (gChoreographers.startTimes.size() >= kMaxStartTimes) { + gChoreographers.startTimes.erase(gChoreographers.startTimes.begin()); + } + gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC); + } +} + +void Choreographer::signalRefreshRateCallbacks(nsecs_t vsyncPeriod) { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.mLastKnownVsync.store(vsyncPeriod); + for (auto c : gChoreographers.ptrs) { + c->scheduleLatestConfigRequest(); + } +} + +int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) { + std::scoped_lock _l(gChoreographers.lock); + const auto iter = gChoreographers.startTimes.find(vsyncId); + if (iter == gChoreographers.startTimes.end()) { + ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId); + return 0; + } + return iter->second; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/nativedisplay/include/nativedisplay/Choreographer.h b/libs/nativedisplay/include/nativedisplay/Choreographer.h new file mode 100644 index 0000000000..bb63f291f9 --- /dev/null +++ b/libs/nativedisplay/include/nativedisplay/Choreographer.h @@ -0,0 +1,138 @@ +/* + * Copyright 2022 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 <gui/DisplayEventDispatcher.h> +#include <private/android/choreographer.h> +#include <utils/Looper.h> + +#include <mutex> +#include <queue> +#include <thread> + +namespace android { +using gui::VsyncEventData; + +struct FrameCallback { + AChoreographer_frameCallback callback; + AChoreographer_frameCallback64 callback64; + AChoreographer_vsyncCallback vsyncCallback; + void* data; + nsecs_t dueTime; + + inline bool operator<(const FrameCallback& rhs) const { + // Note that this is intentionally flipped because we want callbacks due sooner to be at + // the head of the queue + return dueTime > rhs.dueTime; + } +}; + +struct RefreshRateCallback { + AChoreographer_refreshRateCallback callback; + void* data; + bool firstCallbackFired = false; +}; + +class Choreographer; + +/** + * Implementation of AChoreographerFrameCallbackData. + */ +struct ChoreographerFrameCallbackDataImpl { + int64_t frameTimeNanos{0}; + + VsyncEventData vsyncEventData; + + const Choreographer* choreographer; +}; + +class Choreographer : public DisplayEventDispatcher, public MessageHandler { +public: + struct Context { + std::mutex lock; + std::vector<Choreographer*> ptrs GUARDED_BY(lock); + std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock); + bool registeredToDisplayManager GUARDED_BY(lock) = false; + + std::atomic<nsecs_t> mLastKnownVsync = -1; + }; + static Context gChoreographers; + + explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock); + void postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, + AChoreographer_vsyncCallback vsyncCallback, void* data, + nsecs_t delay); + void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) + EXCLUDES(gChoreographers.lock); + void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + // Drains the queue of pending vsync periods and dispatches refresh rate + // updates to callbacks. + // The assumption is that this method is only called on a single + // processing thread, either by looper or by AChoreographer_handleEvents + void handleRefreshRateUpdates(); + void scheduleLatestConfigRequest(); + + enum { + MSG_SCHEDULE_CALLBACKS = 0, + MSG_SCHEDULE_VSYNC = 1, + MSG_HANDLE_REFRESH_RATE_UPDATES = 2, + }; + virtual void handleMessage(const Message& message) override; + + static void initJVM(JNIEnv* env); + static Choreographer* getForThread(); + static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock); + static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock); + virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); + int64_t getFrameInterval() const; + bool inCallback() const; + +private: + Choreographer(const Choreographer&) = delete; + + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) override; + void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, + nsecs_t vsyncPeriod) override; + void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; + void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) override; + + void scheduleCallbacks(); + + ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; + void registerStartTime() const; + + std::mutex mLock; + // Protected by mLock + std::priority_queue<FrameCallback> mFrameCallbacks; + std::vector<RefreshRateCallback> mRefreshRateCallbacks; + + nsecs_t mLatestVsyncPeriod = -1; + VsyncEventData mLastVsyncEventData; + bool mInCallback = false; + + const sp<Looper> mLooper; + const std::thread::id mThreadId; + + // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway. + static constexpr size_t kMaxStartTimes = 250; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index 969d9379f0..9172d5ed13 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -1,25 +1,25 @@ LIBNATIVEDISPLAY { global: - AChoreographer_getInstance; # apex # introduced=30 - AChoreographer_postFrameCallback; # apex # introduced=30 - AChoreographer_postFrameCallbackDelayed; # apex # introduced=30 - AChoreographer_postFrameCallback64; # apex # introduced=30 - AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30 - AChoreographer_registerRefreshRateCallback; # apex # introduced=30 - AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30 - AChoreographer_postVsyncCallback; # apex # introduced=33 - AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33 - AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33 - AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33 - AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33 - AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos; # apex # introduced=33 - AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # apex # introduced=33 - AChoreographer_create; # apex # introduced=30 - AChoreographer_destroy; # apex # introduced=30 - AChoreographer_getFd; # apex # introduced=30 - AChoreographer_handlePendingEvents; # apex # introduced=30 - ASurfaceTexture_fromSurfaceTexture; # apex # introduced=30 - ASurfaceTexture_release; # apex # introduced=30 + AChoreographer_getInstance; # systemapi # introduced=30 + AChoreographer_postFrameCallback; # systemapi # introduced=30 + AChoreographer_postFrameCallbackDelayed; # systemapi # introduced=30 + AChoreographer_postFrameCallback64; # systemapi # introduced=30 + AChoreographer_postFrameCallbackDelayed64; # systemapi # introduced=30 + AChoreographer_registerRefreshRateCallback; # systemapi # introduced=30 + AChoreographer_unregisterRefreshRateCallback; # systemapi # introduced=30 + AChoreographer_postVsyncCallback; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getFrameTimeNanos; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelinesLength; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos; # systemapi # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # systemapi # introduced=33 + AChoreographer_create; # systemapi # introduced=30 + AChoreographer_destroy; # systemapi # introduced=30 + AChoreographer_getFd; # systemapi # introduced=30 + AChoreographer_handlePendingEvents; # systemapi # introduced=30 + ASurfaceTexture_fromSurfaceTexture; # systemapi # introduced=30 + ASurfaceTexture_release; # systemapi # introduced=30 local: *; }; diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 4a1784ea0b..bbafbffae0 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "AHardwareBuffer" +#include <android/hardware_buffer.h> +#include <android/hardware_buffer_aidl.h> #include <vndk/hardware_buffer.h> #include <errno.h> @@ -32,6 +34,9 @@ #include <android/hardware/graphics/common/1.1/types.h> #include <aidl/android/hardware/graphics/common/PixelFormat.h> +// TODO: Better way to handle this +#include "../binder/ndk/parcel_internal.h" + static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints using namespace android; @@ -357,12 +362,12 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out return INVALID_OPERATION; } - GraphicBuffer* gBuffer = new GraphicBuffer(); + sp<GraphicBuffer> gBuffer(new GraphicBuffer()); status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount); if (err != NO_ERROR) { return err; } - *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer); + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer.get()); // Ensure the buffer has a positive ref-count. AHardwareBuffer_acquire(*outBuffer); @@ -412,6 +417,25 @@ int AHardwareBuffer_getId(const AHardwareBuffer* buffer, uint64_t* outId) { return OK; } +binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) { + if (!parcel || !outBuffer) return STATUS_BAD_VALUE; + auto buffer = sp<GraphicBuffer>::make(); + status_t status = parcel->get()->read(*buffer); + if (status != STATUS_OK) return status; + *outBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get()); + AHardwareBuffer_acquire(*outBuffer); + return STATUS_OK; +} + +binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer, + AParcel* _Nonnull parcel) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return STATUS_BAD_VALUE; + if (!parcel) return STATUS_BAD_VALUE; + return parcel->get()->write(*gb); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -678,6 +702,14 @@ uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } +int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) { + GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + auto& mapper = GraphicBufferMapper::get(); + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + mapper.getDataspace(gb->handle, &dataspace); + return static_cast<int32_t>(dataspace); +} + uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { using android::hardware::graphics::common::V1_1::BufferUsage; static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER, diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 18a4b2d3e8..b7b2926c73 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -20,10 +20,15 @@ // from nativewindow/includes/system/window.h // (not to be confused with the compatibility-only window.h from system/core/includes) #include <system/window.h> +#include <android/native_window_aidl.h> #include <private/android/AHardwareBufferHelpers.h> +#include <log/log.h> #include <ui/GraphicBuffer.h> +#include <gui/Surface.h> +#include <gui/view/Surface.h> +#include <android/binder_libbinder.h> using namespace android; @@ -59,6 +64,13 @@ static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) { return false; } } +static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) { + return Surface::getIGraphicBufferProducer(window); +} + +static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) { + return Surface::getSurfaceControlHandle(window); +} /************************************************************************************************** * NDK @@ -176,8 +188,8 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_cast<int>(HAL_DATASPACE_BT2020_HLG)); static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) == static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG)); - static_assert(static_cast<int>(DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH)); - static_assert(static_cast<int>(DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH)); + static_assert(static_cast<int>(ADATASPACE_DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH)); + static_assert(static_cast<int>(ADATASPACE_DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || !isDataSpaceValid(window, dataSpace)) { @@ -193,6 +205,13 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { return query(window, NATIVE_WINDOW_DATASPACE); } +int32_t ANativeWindow_getBuffersDefaultDataSpace(ANativeWindow* window) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { + return -EINVAL; + } + return query(window, NATIVE_WINDOW_DEFAULT_DATASPACE); +} + int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) { return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); @@ -334,6 +353,42 @@ int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation return native_window_set_auto_prerotation(window, autoPrerotation); } +binder_status_t ANativeWindow_readFromParcel( + const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) { + const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + + // Use a android::view::Surface to unparcel the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + status_t ret = shimSurface->readFromParcel(nativeParcel); + if (ret != OK) { + ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__); + return STATUS_BAD_VALUE; + } + sp<Surface> surface = sp<Surface>::make( + shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle); + ANativeWindow* anw = surface.get(); + ANativeWindow_acquire(anw); + *outWindow = anw; + return STATUS_OK; +} + +binder_status_t ANativeWindow_writeToParcel( + ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) { + int value; + int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value); + if (err != OK || value != NATIVE_WINDOW_SURFACE) { + ALOGE("Error: ANativeWindow is not backed by Surface"); + return STATUS_BAD_VALUE; + } + // Use a android::view::Surface to parcelize the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window); + shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window); + + Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + return shimSurface->writeToParcel(nativeParcel); +} + /************************************************************************************************** * apex-stable **************************************************************************************************/ diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index cedc522f3e..bc0bfc52d5 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -102,15 +102,19 @@ cc_library { "liblog", "libutils", "libui", + "libbinder", + "libbinder_ndk", "android.hardware.graphics.common@1.1", ], static_libs: [ "libarect", "libgrallocusage", + "libgui_aidl_static", ], header_libs: [ + "libgui_headers", "libarect_headers", "libnativebase_headers", "libnativewindow_headers", diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index ddfd1d1918..6d3d295a0c 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -52,6 +52,11 @@ uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); // convert HAL format to AHardwareBuffer format (note: this is a no-op) uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); +// retrieves a dataspace from the AHardwareBuffer metadata, if the device +// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not +// supported. +int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer); + // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 771844f4fe..ad4cc4a229 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -548,14 +548,14 @@ enum ADataSpace { * * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB. */ - DEPTH = 4096, + ADATASPACE_DEPTH = 4096, /** * ISO 16684-1:2011(E) Dynamic Depth: * * Embedded depth metadata following the dynamic depth specification. */ - DYNAMIC_DEPTH = 4098 + ADATASPACE_DYNAMIC_DEPTH = 4098 }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h new file mode 100644 index 0000000000..906d9c6f6b --- /dev/null +++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h @@ -0,0 +1,151 @@ +/* + * Copyright 2022 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. + */ + +/** + * @file hardware_buffer_aidl.h + * @brief HardwareBuffer NDK AIDL glue code + */ + +/** + * @addtogroup AHardwareBuffer + * + * Parcelable support for AHardwareBuffer. Can be used with libbinder_ndk + * + * @{ + */ + +#ifndef ANDROID_HARDWARE_BUFFER_AIDL_H +#define ANDROID_HARDWARE_BUFFER_AIDL_H + +#include <android/binder_parcel.h> +#include <android/hardware_buffer.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Read an AHardwareBuffer from a AParcel. The output buffer will have an + * initial reference acquired and will need to be released with + * AHardwareBuffer_release. + * + * Available since API level 34. + * + * \return STATUS_OK on success + * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an + * issue deserializing (eg, corrupted parcel) + * STATUS_BAD_TYPE if the parcel's current data position is not that of + * an AHardwareBuffer type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(34); + +/** + * Write an AHardwareBuffer to an AParcel. + * + * Available since API level 34. + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either buffer or parcel is null, or if the AHardwareBuffer* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer, + AParcel* _Nonnull parcel) __INTRODUCED_IN(34); + +__END_DECLS + +// Only enable the AIDL glue helper if this is C++ +#ifdef __cplusplus + +namespace aidl::android::hardware { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the AHardwareBuffer* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class HardwareBuffer { +public: + HardwareBuffer() noexcept {} + explicit HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {} + + ~HardwareBuffer() { + reset(); + } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + return AHardwareBuffer_readFromParcel(parcel, &mBuffer); + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mBuffer) { + return STATUS_BAD_VALUE; + } + return AHardwareBuffer_writeToParcel(mBuffer, parcel); + } + + /** + * Destroys any currently owned AHardwareBuffer* and takes ownership of the given + * AHardwareBuffer* + * + * @param buffer The buffer to take ownership of + */ + void reset(AHardwareBuffer* _Nullable buffer = nullptr) noexcept { + if (mBuffer) { + AHardwareBuffer_release(mBuffer); + mBuffer = nullptr; + } + mBuffer = buffer; + } + + inline AHardwareBuffer* _Nullable operator-> () const { return mBuffer; } + inline AHardwareBuffer* _Nullable get() const { return mBuffer; } + inline explicit operator bool () const { return mBuffer != nullptr; } + + HardwareBuffer& operator=(HardwareBuffer&& other) noexcept { + reset(other.release()); + return *this; + } + + /** + * Stops managing any contained AHardwareBuffer*, returning it to the caller. Ownership + * is released. + * @return AHardwareBuffer* or null if this was empty + */ + [[nodiscard]] AHardwareBuffer* _Nullable release() noexcept { + AHardwareBuffer* _Nullable ret = mBuffer; + mBuffer = nullptr; + return ret; + } + +private: + HardwareBuffer(const HardwareBuffer& other) = delete; + HardwareBuffer& operator=(const HardwareBuffer& other) = delete; + + AHardwareBuffer* _Nullable mBuffer = nullptr; +}; + +} // aidl::android::hardware + +#endif // __cplusplus + +#endif // ANDROID_HARDWARE_BUFFER_AIDL_H + +/** @} */ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index f0e1c4d749..be6623ee75 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -227,6 +227,16 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa */ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN(28); +/** + * Get the default dataspace of the buffers in window as set by the consumer. + * + * Available since API level 34. + * + * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if + * dataspace is unknown, or -EINVAL if window is invalid. + */ +int32_t ANativeWindow_getBuffersDefaultDataSpace(ANativeWindow* window) __INTRODUCED_IN(34); + /** Compatibility value for ANativeWindow_setFrameRate. */ enum ANativeWindow_FrameRateCompatibility { /** @@ -303,6 +313,8 @@ enum ANativeWindow_ChangeFrameRateStrategy { * You can register for changes in the refresh rate using * \a AChoreographer_registerRefreshRateCallback. * + * See ANativeWindow_clearFrameRate(). + * * Available since API level 31. * * \param window pointer to an ANativeWindow object. @@ -332,8 +344,43 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa int8_t compatibility, int8_t changeFrameRateStrategy) __INTRODUCED_IN(31); +/** + * Clears the frame rate which is set for this window. + * + * This is equivalent to calling + * ANativeWindow_setFrameRateWithChangeStrategy(window, 0, compatibility, changeFrameRateStrategy). + * + * Usage of this API won't introduce frame rate throttling, + * or affect other aspects of the application's frame production + * pipeline. However, because the system may change the display refresh rate, + * calls to this function may result in changes to Choreographer callback + * timings, and changes to the time interval at which the system releases + * buffers back to the application. + * + * Note that this only has an effect for windows presented on the display. If + * this ANativeWindow is consumed by something other than the system compositor, + * e.g. a media codec, this call has no effect. + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ANativeWindow_setFrameRateWithChangeStrategy(). + * + * Available since API level 34. + * + * \param window pointer to an ANativeWindow object. + * + * \return 0 for success, -EINVAL if the window value is invalid. + */ +inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) + __INTRODUCED_IN(__ANDROID_API_U__) { + return ANativeWindow_setFrameRateWithChangeStrategy(window, 0, + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); +} + #ifdef __cplusplus -}; +} #endif #endif // ANDROID_NATIVE_WINDOW_H diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h new file mode 100644 index 0000000000..a252245a10 --- /dev/null +++ b/libs/nativewindow/include/android/native_window_aidl.h @@ -0,0 +1,161 @@ +/* + * Copyright 2022 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. + */ + +/** + * @file native_window_aidl.h + * @brief NativeWindow NDK AIDL glue code + */ + +/** + * @addtogroup ANativeWindow + * + * Parcelable support for ANativeWindow. Can be used with libbinder_ndk + * + * @{ + */ + +#ifndef ANDROID_NATIVE_WINDOW_AIDL_H +#define ANDROID_NATIVE_WINDOW_AIDL_H + +#include <android/binder_parcel.h> +#include <android/native_window.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Read an ANativeWindow from a AParcel. The output buffer will have an + * initial reference acquired and will need to be released with + * ANativeWindow_release. + * + * Available since API level 34. + * + * \return STATUS_OK on success + * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an + * issue deserializing (eg, corrupted parcel) + * STATUS_BAD_TYPE if the parcel's current data position is not that of + * an ANativeWindow type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t ANativeWindow_readFromParcel(const AParcel* _Nonnull parcel, + ANativeWindow* _Nullable* _Nonnull outWindow) __INTRODUCED_IN(__ANDROID_API_U__); + +/** + * Write an ANativeWindow to an AParcel. + * + * Available since API level 34. + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either buffer or parcel is null, or if the ANativeWindow* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t ANativeWindow_writeToParcel(ANativeWindow* _Nonnull window, + AParcel* _Nonnull parcel) __INTRODUCED_IN(__ANDROID_API_U__); + +__END_DECLS + +// Only enable the AIDL glue helper if this is C++ +#ifdef __cplusplus + +namespace aidl::android::hardware { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the ANativeWindow* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class NativeWindow { +public: + NativeWindow() noexcept {} + explicit NativeWindow(ANativeWindow* _Nullable window) { + reset(window); + } + + explicit NativeWindow(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + } + + ~NativeWindow() { + reset(); + } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + return ANativeWindow_readFromParcel(parcel, &mWindow); + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mWindow) { + return STATUS_BAD_VALUE; + } + return ANativeWindow_writeToParcel(mWindow, parcel); + } + + /** + * Destroys any currently owned ANativeWindow* and takes ownership of the given + * ANativeWindow* + * + * @param buffer The buffer to take ownership of + */ + void reset(ANativeWindow* _Nullable window = nullptr) noexcept { + if (mWindow) { + ANativeWindow_release(mWindow); + mWindow = nullptr; + } + if (window != nullptr) { + ANativeWindow_acquire(window); + } + mWindow = window; + } + inline ANativeWindow* _Nullable operator-> () const { return mWindow; } + inline ANativeWindow* _Nullable get() const { return mWindow; } + inline explicit operator bool () const { return mWindow != nullptr; } + + NativeWindow& operator=(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + return *this; + } + + /** + * Stops managing any contained ANativeWindow*, returning it to the caller. Ownership + * is released. + * @return ANativeWindow* or null if this was empty + */ + [[nodiscard]] ANativeWindow* _Nullable release() noexcept { + ANativeWindow* _Nullable ret = mWindow; + mWindow = nullptr; + return ret; + } +private: + ANativeWindow* _Nullable mWindow = nullptr; + NativeWindow(const NativeWindow &other) = delete; + NativeWindow& operator=(const NativeWindow &other) = delete; +}; + +} // aidl::android::hardware + // +namespace aidl::android::view { + using Surface = aidl::android::hardware::NativeWindow; +} + +#endif // __cplusplus + +#endif // ANDROID_NATIVE_WINDOW_AIDL_H + +/** @} */ diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index a54af1fa62..c7745e6672 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -235,8 +235,8 @@ enum { NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25, NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27, - NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, - NATIVE_WINDOW_GET_HDR_SUPPORT = 29, + /* 28, removed: NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT */ + /* 29, removed: NATIVE_WINDOW_GET_HDR_SUPPORT */ NATIVE_WINDOW_SET_USAGE64 = ANATIVEWINDOW_PERFORM_SET_USAGE64, NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, @@ -988,15 +988,34 @@ static inline int native_window_get_frame_timestamps( outDequeueReadyTime, outReleaseTime); } -static inline int native_window_get_wide_color_support( - struct ANativeWindow* window, bool* outSupport) { - return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT, - outSupport); +/* deprecated. Always returns 0 and outSupport holds true. Don't call. */ +static inline int native_window_get_wide_color_support ( + struct ANativeWindow* window __UNUSED, bool* outSupport) __deprecated; + +/* + Deprecated(b/242763577): to be removed, this method should not be used + Surface support should not be tied to the display + Return true since most displays should have this support +*/ +static inline int native_window_get_wide_color_support ( + struct ANativeWindow* window __UNUSED, bool* outSupport) { + *outSupport = true; + return 0; } -static inline int native_window_get_hdr_support(struct ANativeWindow* window, +/* deprecated. Always returns 0 and outSupport holds true. Don't call. */ +static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED, + bool* outSupport) __deprecated; + +/* + Deprecated(b/242763577): to be removed, this method should not be used + Surface support should not be tied to the display + Return true since most displays should have this support +*/ +static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED, bool* outSupport) { - return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport); + *outSupport = true; + return 0; } static inline int native_window_get_consumer_usage(struct ANativeWindow* window, @@ -1034,6 +1053,11 @@ enum { * This surface is ignored while choosing the refresh rate. */ ANATIVEWINDOW_FRAME_RATE_NO_VOTE, + + /** + * This surface will vote for the minimum refresh rate. + */ + ANATIVEWINDOW_FRAME_RATE_MIN }; static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate, diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index da42a96df7..c2fd6efcdb 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -14,6 +14,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket; AHardwareBuffer_unlock; + AHardwareBuffer_readFromParcel; # introduced=34 + AHardwareBuffer_writeToParcel; # introduced=34 ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk @@ -21,6 +23,7 @@ LIBNATIVEWINDOW { ANativeWindow_cancelBuffer; # llndk ANativeWindow_dequeueBuffer; # llndk ANativeWindow_getBuffersDataSpace; # introduced=28 + ANativeWindow_getBuffersDefaultDataSpace; # introduced=34 ANativeWindow_getFormat; ANativeWindow_getHeight; ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 @@ -53,6 +56,8 @@ LIBNATIVEWINDOW { ANativeWindow_setUsage; # llndk ANativeWindow_tryAllocateBuffers; # introduced=30 ANativeWindow_unlockAndPost; + ANativeWindow_readFromParcel; # introduced=UpsideDownCake + ANativeWindow_writeToParcel; # introduced=UpsideDownCake local: *; }; @@ -65,6 +70,7 @@ LIBNATIVEWINDOW_PLATFORM { android::AHardwareBuffer_convertToPixelFormat*; android::AHardwareBuffer_convertFromGrallocUsageBits*; android::AHardwareBuffer_convertToGrallocUsageBits*; + android::AHardwareBuffer_getDataSpace*; android::AHardwareBuffer_to_GraphicBuffer*; android::AHardwareBuffer_to_ANativeWindowBuffer*; android::AHardwareBuffer_from_GraphicBuffer*; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index f6f57dde7d..04e24ed9ed 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -21,13 +21,15 @@ cc_defaults { cc_defaults { name: "librenderengine_defaults", - defaults: ["renderengine_defaults"], + defaults: [ + "android.hardware.graphics.composer3-ndk_shared", + "renderengine_defaults", + ], cflags: [ "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", ], shared_libs: [ - "android.hardware.graphics.composer3-V1-ndk", "libbase", "libcutils", "libEGL", @@ -40,6 +42,7 @@ cc_defaults { "libsync", "libui", "libutils", + "libvulkan", ], static_libs: [ @@ -95,6 +98,7 @@ filegroup { "skia/ColorSpaces.cpp", "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", + "skia/SkiaVkRenderEngine.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index c7ad058ab9..341c011dc9 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -19,9 +19,11 @@ #include <cutils/properties.h> #include <log/log.h> #include "gl/GLESRenderEngine.h" +#include "renderengine/ExternalTexture.h" #include "threaded/RenderEngineThreaded.h" #include "skia/SkiaGLRenderEngine.h" +#include "skia/SkiaVkRenderEngine.h" namespace android { namespace renderengine { @@ -36,6 +38,13 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg case RenderEngineType::SKIA_GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); + case RenderEngineType::SKIA_VK: +#ifdef RE_SKIAVK + ALOGD("RenderEngine with SkiaVK Backend"); + return renderengine::skia::SkiaVkRenderEngine::create(args); +#else + LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!"); +#endif case RenderEngineType::SKIA_GL_THREADED: { ALOGD("Threaded RenderEngine with SkiaGL Backend"); return renderengine::threaded::RenderEngineThreaded::create( @@ -44,6 +53,17 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg }, args.renderEngineType); } + case RenderEngineType::SKIA_VK_THREADED: +#ifdef RE_SKIAVK + ALOGD("Threaded RenderEngine with SkiaVK Backend"); + return renderengine::threaded::RenderEngineThreaded::create( + [args]() { + return android::renderengine::skia::SkiaVkRenderEngine::create(args); + }, + args.renderEngineType); +#else + LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!"); +#endif case RenderEngineType::GLES: default: ALOGD("RenderEngine with GLES Backend"); @@ -63,16 +83,29 @@ void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) { "output buffer not gpu writeable"); } -std::future<RenderEngineResult> RenderEngine::drawLayers( - const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) { - const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>(); - std::future<RenderEngineResult> resultFuture = resultPromise->get_future(); +ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence) { + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); + updateProtectedContext(layers, buffer); drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, std::move(bufferFence)); return resultFuture; } +void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer) { + const bool needsProtectedContext = + (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) || + std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) { + const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer; + return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + }); + useProtectedContext(needsProtectedContext); +} + } // namespace renderengine } // namespace android diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index 249fec5866..afbe6cfa4d 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -24,6 +24,7 @@ package { cc_benchmark { name: "librenderengine_bench", defaults: [ + "android.hardware.graphics.composer3-ndk_shared", "skia_deps", "surfaceflinger_defaults", ], @@ -43,7 +44,6 @@ cc_benchmark { ], shared_libs: [ - "android.hardware.graphics.composer3-V1-ndk", "libbase", "libcutils", "libjnigraphics", diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index ead97cf5df..bd7b617ae7 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -39,6 +39,10 @@ std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { return "skiaglthreaded"; case RenderEngine::RenderEngineType::SKIA_GL: return "skiagl"; + case RenderEngine::RenderEngineType::SKIA_VK: + return "skiavk"; + case RenderEngine::RenderEngineType::SKIA_VK_THREADED: + return "skiavkthreaded"; case RenderEngine::RenderEngineType::GLES: case RenderEngine::RenderEngineType::THREADED: LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?"); @@ -80,16 +84,26 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { std::once_flag once; std::call_once(once, []() { auto surfaceComposerClient = SurfaceComposerClient::getDefault(); - auto displayToken = surfaceComposerClient->getInternalDisplayToken(); - ui::DisplayMode displayMode; - if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) { - LOG_ALWAYS_FATAL("Failed to get active display mode!"); + auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!"); + ui::Size resolution = ui::kEmptySize; + // find the largest display resolution + for (auto id : ids) { + auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id); + ui::DisplayMode displayMode; + if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) { + LOG_ALWAYS_FATAL("Failed to get active display mode!"); + } + auto tw = displayMode.resolution.width; + auto th = displayMode.resolution.height; + LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!"); + if (resolution.width * resolution.height < + displayMode.resolution.width * displayMode.resolution.height) { + resolution = displayMode.resolution; + } } - auto w = displayMode.resolution.width; - auto h = displayMode.resolution.height; - LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!"); - width = static_cast<uint32_t>(w); - height = static_cast<uint32_t>(h); + width = static_cast<uint32_t>(resolution.width); + height = static_cast<uint32_t>(resolution.height); }); return std::pair<uint32_t, uint32_t>(width, height); } @@ -117,11 +131,12 @@ static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_ uint64_t extraUsageFlags = 0, std::string name = "output") { return std::make_shared< - impl::ExternalTexture>(new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - extraUsageFlags, - std::move(name)), + impl::ExternalTexture>(sp<GraphicBuffer>::make(width, height, + HAL_PIXEL_FORMAT_RGBA_8888, 1u, + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + extraUsageFlags, + std::move(name)), re, impl::ExternalTexture::Usage::READABLE | impl::ExternalTexture::Usage::WRITEABLE); @@ -158,9 +173,10 @@ static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re, }; auto layers = std::vector<LayerSettings>{layer}; - auto [status, drawFence] = - re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get(); - sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence)); + sp<Fence> waitFence = + re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()) + .get() + .value(); waitFence->waitForever(LOG_TAG); return texture; } @@ -189,10 +205,10 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& // This loop starts and stops the timer. for (auto _ : benchState) { - auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer, - kUseFrameBufferCache, base::unique_fd()) - .get(); - sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence)); + sp<Fence> waitFence = re.drawLayers(display, layers, outputBuffer, kUseFrameBufferCache, + base::unique_fd()) + .get() + .value(); waitFence->waitForever(LOG_TAG); } diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 6dc01b916e..13f766c3e1 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -454,8 +454,9 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mImageManager->initThread(); mDrawingBuffer = createFramebuffer(); sp<GraphicBuffer> buf = - new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder"); + sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, + "placeholder"); const status_t err = buf->initCheck(); if (err != OK) { @@ -1080,14 +1081,14 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer } void GLESRenderEngine::drawLayersInternal( - const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence) { ATRACE_CALL(); if (layers.empty()) { ALOGV("Drawing empty layer stack"); - resultPromise->set_value({NO_ERROR, base::unique_fd()}); + resultPromise->set_value(Fence::NO_FENCE); return; } @@ -1102,7 +1103,7 @@ void GLESRenderEngine::drawLayersInternal( if (buffer == nullptr) { ALOGE("No output buffer provided. Aborting GPU composition."); - resultPromise->set_value({BAD_VALUE, base::unique_fd()}); + resultPromise->set_value(base::unexpected(BAD_VALUE)); return; } @@ -1131,7 +1132,7 @@ void GLESRenderEngine::drawLayersInternal( ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors(); - resultPromise->set_value({fbo->getStatus(), base::unique_fd()}); + resultPromise->set_value(base::unexpected(fbo->getStatus())); return; } setViewportAndProjection(display.physicalDisplay, display.clip); @@ -1143,7 +1144,7 @@ void GLESRenderEngine::drawLayersInternal( ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors(); - resultPromise->set_value({status, base::unique_fd()}); + resultPromise->set_value(base::unexpected(status)); return; } } @@ -1177,7 +1178,7 @@ void GLESRenderEngine::drawLayersInternal( ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't render first blur pass"); - resultPromise->set_value({status, base::unique_fd()}); + resultPromise->set_value(base::unexpected(status)); return; } @@ -1200,7 +1201,7 @@ void GLESRenderEngine::drawLayersInternal( ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't bind native framebuffer"); - resultPromise->set_value({status, base::unique_fd()}); + resultPromise->set_value(base::unexpected(status)); return; } @@ -1209,7 +1210,7 @@ void GLESRenderEngine::drawLayersInternal( ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't render blur filter"); - resultPromise->set_value({status, base::unique_fd()}); + resultPromise->set_value(base::unexpected(status)); return; } } @@ -1309,7 +1310,7 @@ void GLESRenderEngine::drawLayersInternal( checkErrors(); // Chances are, something illegal happened (either the caller passed // us bad parameters, or we messed up our shader generation). - resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)}); + resultPromise->set_value(base::unexpected(INVALID_OPERATION)); return; } mLastDrawFence = nullptr; @@ -1321,8 +1322,7 @@ void GLESRenderEngine::drawLayersInternal( mPriorResourcesCleaned = false; checkErrors(); - resultPromise->set_value({NO_ERROR, std::move(drawFence)}); - return; + resultPromise->set_value(sp<Fence>::make(std::move(drawFence))); } void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 1d7c2cafb5..1b3492154b 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -31,6 +31,7 @@ #include <renderengine/RenderEngine.h> #include <renderengine/private/Description.h> #include <sys/types.h> +#include <ui/FenceResult.h> #include "GLShadowTexture.h" #include "ImageManager.h" @@ -60,7 +61,7 @@ public: std::future<void> primeCache() override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; - bool isProtected() const override { return mInProtectedContext; } + bool isProtected() const { return mInProtectedContext; } bool supportsProtectedContent() const override; void useProtectedContext(bool useProtectedContext) override; void cleanupPostRender() override; @@ -102,7 +103,7 @@ protected: EXCLUDES(mRenderingMutex); void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); bool canSkipPostRenderCleanup() const override; - void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp index 5ff92402dc..f7f2d54515 100644 --- a/libs/renderengine/gl/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -601,7 +601,7 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { } if (needs.hasTextureCoords()) { - fs << "varying vec2 outTexCoords;"; + fs << "varying highp vec2 outTexCoords;"; } if (needs.hasRoundedCorners()) { diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h new file mode 100644 index 0000000000..0ee6661f33 --- /dev/null +++ b/libs/renderengine/include/renderengine/BorderRenderInfo.h @@ -0,0 +1,36 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <math/mat4.h> +#include <ui/Region.h> + +namespace android { +namespace renderengine { + +struct BorderRenderInfo { + float width = 0; + half4 color; + Region combinedRegion; + + bool operator==(const BorderRenderInfo& rhs) const { + return (width == rhs.width && color == rhs.color && + combinedRegion.hasSameRects(rhs.combinedRegion)); + } +}; + +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index 59ef991eec..8d7c13cb18 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -22,17 +22,25 @@ #include <math/mat4.h> #include <renderengine/PrintMatrix.h> +#include <renderengine/BorderRenderInfo.h> +#include <ui/DisplayId.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> +#include <optional> + namespace android { namespace renderengine { // DisplaySettings contains the settings that are applicable when drawing all // layers for a given display. struct DisplaySettings { + // A string containing the name of the display, along with its id, if it has + // one. + std::string namePlusId; + // Rectangle describing the physical display. We will project from the // logical clip onto this rectangle. Rect physicalDisplay = Rect::INVALID_RECT; @@ -79,18 +87,21 @@ struct DisplaySettings { // Configures the rendering intent of the output display. This is used for tonemapping. aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC; + + std::vector<renderengine::BorderRenderInfo> borderInfoList; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { - return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip && - lhs.maxLuminance == rhs.maxLuminance && + return lhs.namePlusId == rhs.namePlusId && lhs.physicalDisplay == rhs.physicalDisplay && + lhs.clip == rhs.clip && lhs.maxLuminance == rhs.maxLuminance && lhs.currentLuminanceNits == rhs.currentLuminanceNits && lhs.outputDataspace == rhs.outputDataspace && lhs.colorTransform == rhs.colorTransform && lhs.deviceHandlesColorTransform == rhs.deviceHandlesColorTransform && lhs.orientation == rhs.orientation && lhs.targetLuminanceNits == rhs.targetLuminanceNits && - lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent; + lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent && + lhs.borderInfoList == rhs.borderInfoList; } static const char* orientation_to_string(uint32_t orientation) { @@ -117,6 +128,7 @@ static const char* orientation_to_string(uint32_t orientation) { static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) { *os << "DisplaySettings {"; + *os << "\n .display = " << settings.namePlusId; *os << "\n .physicalDisplay = "; PrintTo(settings.physicalDisplay, os); *os << "\n .clip = "; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 3e7f69ce02..39621cd080 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -18,6 +18,7 @@ #define SF_RENDERENGINE_H_ #include <android-base/unique_fd.h> +#include <ftl/future.h> #include <math/mat4.h> #include <renderengine/DisplaySettings.h> #include <renderengine/ExternalTexture.h> @@ -26,6 +27,7 @@ #include <renderengine/LayerSettings.h> #include <stdint.h> #include <sys/types.h> +#include <ui/FenceResult.h> #include <ui/GraphicTypes.h> #include <ui/Transform.h> @@ -68,7 +70,6 @@ class Image; class Mesh; class Texture; struct RenderEngineCreationArgs; -struct RenderEngineResult; namespace threaded { class RenderEngineThreaded; @@ -98,6 +99,8 @@ public: THREADED = 2, SKIA_GL = 3, SKIA_GL_THREADED = 4, + SKIA_VK = 5, + SKIA_VK_THREADED = 6, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); @@ -125,12 +128,8 @@ public: // ----- BEGIN NEW INTERFACE ----- // queries that are required to be thread safe - virtual bool isProtected() const = 0; virtual bool supportsProtectedContent() const = 0; - // Attempt to switch RenderEngine into and out of protectedContext mode - virtual void useProtectedContext(bool useProtectedContext) = 0; - // Notify RenderEngine of changes to the dimensions of the active display // so that it can configure its internal caches accordingly. virtual void onActiveDisplaySizeChanged(ui::Size size) = 0; @@ -158,12 +157,13 @@ public: // parameter does nothing. // @param bufferFence Fence signalling that the buffer is ready to be drawn // to. - // @return A future object of RenderEngineResult struct indicating whether - // drawing was successful in async mode. - virtual std::future<RenderEngineResult> drawLayers( - const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence); + // @return A future object of FenceResult indicating whether drawing was + // successful in async mode. + virtual ftl::Future<FenceResult> drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence); // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up @@ -172,9 +172,16 @@ public: virtual void cleanupPostRender() = 0; virtual void cleanFramebufferCache() = 0; - // Returns the priority this context was actually created with. Note: this may not be - // the same as specified at context creation time, due to implementation limits on the - // number of contexts that can be created at a specific priority level in the system. + + // Returns the priority this context was actually created with. Note: this + // may not be the same as specified at context creation time, due to + // implementation limits on the number of contexts that can be created at a + // specific priority level in the system. + // + // This should return a valid EGL context priority enum as described by + // https://registry.khronos.org/EGL/extensions/IMG/EGL_IMG_context_priority.txt + // or + // https://registry.khronos.org/EGL/extensions/NV/EGL_NV_context_priority_realtime.txt virtual int getContextPriority() = 0; // Returns true if blur was requested in the RenderEngineCreationArgs and the implementation @@ -236,8 +243,15 @@ protected: friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; const RenderEngineType mRenderEngineType; + // Update protectedContext mode depending on whether or not any layer has a protected buffer. + void updateProtectedContext(const std::vector<LayerSettings>&, + const std::shared_ptr<ExternalTexture>&); + + // Attempt to switch RenderEngine into and out of protectedContext mode + virtual void useProtectedContext(bool useProtectedContext) = 0; + virtual void drawLayersInternal( - const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence) = 0; @@ -327,13 +341,6 @@ private: RenderEngine::RenderEngineType::SKIA_GL_THREADED; }; -struct RenderEngineResult { - // status indicates if drawing is successful - status_t status; - // drawFence will fire when the buffer has been drawn to and is ready to be examined. - base::unique_fd drawFence; -}; - } // namespace renderengine } // namespace android diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 248bd652c0..e3ce85dd07 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -48,14 +48,13 @@ public: MOCK_METHOD0(cleanupPostRender, void()); MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool()); MOCK_METHOD5(drawLayers, - std::future<RenderEngineResult>(const DisplaySettings&, - const std::vector<LayerSettings>&, - const std::shared_ptr<ExternalTexture>&, - const bool, base::unique_fd&&)); + ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, + const std::shared_ptr<ExternalTexture>&, const bool, + base::unique_fd&&)); MOCK_METHOD6(drawLayersInternal, - void(const std::shared_ptr<std::promise<RenderEngineResult>>&&, - const DisplaySettings&, const std::vector<LayerSettings>&, - const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&)); + void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, + const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, + const bool, base::unique_fd&&)); MOCK_METHOD0(cleanFramebufferCache, void()); MOCK_METHOD0(getContextPriority, int()); MOCK_METHOD0(supportsBackgroundBlur, bool()); diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index c39f0a97fd..f6b91839a3 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -364,8 +364,8 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; sp<GraphicBuffer> dstBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, - 1, usage, "primeShaderCache_dst"); + sp<GraphicBuffer>::make(displayRect.width(), displayRect.height(), + PIXEL_FORMAT_RGBA_8888, 1, usage, "primeShaderCache_dst"); const auto dstTexture = std::make_shared<impl::ExternalTexture>(dstBuffer, *renderengine, @@ -375,8 +375,8 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { // something, but the details are not important. Make use of the shadow layer drawing step // to populate it. sp<GraphicBuffer> srcBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, - 1, usage, "drawImageLayer_src"); + sp<GraphicBuffer>::make(displayRect.width(), displayRect.height(), + PIXEL_FORMAT_RGBA_8888, 1, usage, "drawImageLayer_src"); const auto srcTexture = std::make_shared< impl::ExternalTexture>(srcBuffer, *renderengine, @@ -398,8 +398,9 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { // GRALLOC_USAGE_HW_TEXTURE should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE. const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; sp<GraphicBuffer> externalBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, - 1, usageExternal, "primeShaderCache_external"); + sp<GraphicBuffer>::make(displayRect.width(), displayRect.height(), + PIXEL_FORMAT_RGBA_8888, 1, usageExternal, + "primeShaderCache_external"); const auto externalTexture = std::make_shared<impl::ExternalTexture>(externalBuffer, *renderengine, impl::ExternalTexture::Usage::READABLE); @@ -409,8 +410,9 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { // Another external texture with a different pixel format triggers useIsOpaqueWorkaround. // It doesn't have to be f16, but it can't be the usual 8888. sp<GraphicBuffer> f16ExternalBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16, - 1, usageExternal, "primeShaderCache_external_f16"); + sp<GraphicBuffer>::make(displayRect.width(), displayRect.height(), + PIXEL_FORMAT_RGBA_FP16, 1, usageExternal, + "primeShaderCache_external_f16"); // The F16 texture may not be usable on all devices, so check first that it was created. status_t error = f16ExternalBuffer->initCheck(); if (!error) { diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 0caa9f2fbd..ff598e7ab5 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -24,24 +24,11 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GrContextOptions.h> -#include <SkCanvas.h> -#include <SkColorFilter.h> -#include <SkColorMatrix.h> -#include <SkColorSpace.h> -#include <SkGraphics.h> -#include <SkImage.h> -#include <SkImageFilters.h> -#include <SkRegion.h> -#include <SkShadowUtils.h> -#include <SkSurface.h> #include <android-base/stringprintf.h> #include <gl/GrGLInterface.h> #include <gui/TraceUtils.h> #include <sync/sync.h> -#include <ui/BlurRegion.h> -#include <ui/DataspaceUtils.h> #include <ui/DebugUtils.h> -#include <ui/GraphicBuffer.h> #include <utils/Trace.h> #include <cmath> @@ -50,25 +37,7 @@ #include <numeric> #include "../gl/GLExtensions.h" -#include "Cache.h" -#include "ColorSpaces.h" -#include "SkBlendMode.h" -#include "SkImageInfo.h" -#include "filters/BlurFilter.h" -#include "filters/GaussianBlurFilter.h" -#include "filters/KawaseBlurFilter.h" -#include "filters/LinearEffect.h" #include "log/log_main.h" -#include "skia/debug/SkiaCapture.h" -#include "skia/debug/SkiaMemoryReporter.h" -#include "skia/filters/StretchShaderFactory.h" -#include "system/graphics-base-v1.0.h" - -namespace { -// Debugging settings -static const bool kPrintLayerSettings = false; -static const bool kFlushAfterEveryLayer = kPrintLayerSettings; -} // namespace bool checkGlError(const char* op, int lineNumber); @@ -224,9 +193,10 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( } // initialize the renderer while GL is current - std::unique_ptr<SkiaGLRenderEngine> engine = - std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext, - protectedPlaceholder); + std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt, + placeholder, protectedContext, + protectedPlaceholder)); + engine->ensureGrContextsCreated(); ALOGI("OpenGL ES informations:"); ALOGI("vendor : %s", extensions.getVendor()); @@ -239,11 +209,6 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( return engine; } -std::future<void> SkiaGLRenderEngine::primeCache() { - Cache::primeShaderCache(this); - return {}; -} - EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { status_t err; EGLConfig config; @@ -283,72 +248,20 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo return config; } -sk_sp<SkData> SkiaGLRenderEngine::SkSLCacheMonitor::load(const SkData& key) { - // This "cache" does not actually cache anything. It just allows us to - // monitor Skia's internal cache. So this method always returns null. - return nullptr; -} - -void SkiaGLRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data, - const SkString& description) { - mShadersCachedSinceLastCall++; - mTotalShadersCompiled++; - ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled); -} - -int SkiaGLRenderEngine::reportShadersCompiled() { - return mSkSLCacheMonitor.totalShadersCompiled(); -} - SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder) - : SkiaRenderEngine(args.renderEngineType), + : SkiaRenderEngine(args.renderEngineType, + static_cast<PixelFormat>(args.pixelFormat), + args.useColorManagement, args.supportsBackgroundBlur), mEGLDisplay(display), mEGLContext(ctxt), mPlaceholderSurface(placeholder), mProtectedEGLContext(protectedContext), - mProtectedPlaceholderSurface(protectedPlaceholder), - mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)), - mUseColorManagement(args.useColorManagement) { - sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); - LOG_ALWAYS_FATAL_IF(!glInterface.get()); - - GrContextOptions options; - options.fDisableDriverCorrectnessWorkarounds = true; - options.fDisableDistanceFieldPaths = true; - options.fReducedShaderVariations = true; - options.fPersistentCache = &mSkSLCacheMonitor; - mGrContext = GrDirectContext::MakeGL(glInterface, options); - if (supportsProtectedContent()) { - useProtectedContext(true); - mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options); - useProtectedContext(false); - } - - if (args.supportsBackgroundBlur) { - ALOGD("Background Blurs Enabled"); - mBlurFilter = new KawaseBlurFilter(); - } - mCapture = std::make_unique<SkiaCapture>(); -} + mProtectedPlaceholderSurface(protectedPlaceholder) { } SkiaGLRenderEngine::~SkiaGLRenderEngine() { - std::lock_guard<std::mutex> lock(mRenderingMutex); - if (mBlurFilter) { - delete mBlurFilter; - } - - mCapture = nullptr; - - mGrContext->flushAndSubmit(true); - mGrContext->abandonContext(); - - if (mProtectedGrContext) { - mProtectedGrContext->flushAndSubmit(true); - mProtectedGrContext->abandonContext(); - } - + finishRenderingAndAbandonContext(); if (mPlaceholderSurface != EGL_NO_SURFACE) { eglDestroySurface(mEGLDisplay, mPlaceholderSurface); } @@ -366,71 +279,69 @@ SkiaGLRenderEngine::~SkiaGLRenderEngine() { eglReleaseThread(); } -bool SkiaGLRenderEngine::supportsProtectedContent() const { - return mProtectedEGLContext != EGL_NO_CONTEXT; -} +SkiaRenderEngine::Contexts SkiaGLRenderEngine::createDirectContexts( + const GrContextOptions& options) { -GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const { - return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get(); -} + LOG_ALWAYS_FATAL_IF(isProtected(), + "Cannot setup contexts while already in protected mode"); -void SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { - if (useProtectedContext == mInProtectedContext || - (useProtectedContext && !supportsProtectedContent())) { - return; - } + sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface(); - // release any scratch resources before switching into a new mode - if (getActiveGrContext()) { - getActiveGrContext()->purgeUnlockedResources(true); - } + LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed"); - const EGLSurface surface = - useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface; - const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; - - if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) { - mInProtectedContext = useProtectedContext; - // given that we are sharing the same thread between two GrContexts we need to - // make sure that the thread state is reset when switching between the two. - if (getActiveGrContext()) { - getActiveGrContext()->resetContext(); - } + SkiaRenderEngine::Contexts contexts; + contexts.first = GrDirectContext::MakeGL(glInterface, options); + if (supportsProtectedContentImpl()) { + useProtectedContextImpl(GrProtected::kYes); + contexts.second = GrDirectContext::MakeGL(glInterface, options); + useProtectedContextImpl(GrProtected::kNo); } -} -base::unique_fd SkiaGLRenderEngine::flush() { - ATRACE_CALL(); - if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) { - return base::unique_fd(); - } - - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); - return base::unique_fd(); - } + return contexts; +} - // native fence fd will not be populated until flush() is done. - glFlush(); +bool SkiaGLRenderEngine::supportsProtectedContentImpl() const { + return mProtectedEGLContext != EGL_NO_CONTEXT; +} - // get the fence fd - base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); - eglDestroySyncKHR(mEGLDisplay, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); - } +bool SkiaGLRenderEngine::useProtectedContextImpl(GrProtected isProtected) { + const EGLSurface surface = + (isProtected == GrProtected::kYes) ? + mProtectedPlaceholderSurface : mPlaceholderSurface; + const EGLContext context = (isProtected == GrProtected::kYes) ? + mProtectedEGLContext : mEGLContext; - return fenceFd; + return eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; } -void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) { +void SkiaGLRenderEngine::waitFence(GrDirectContext*, base::borrowed_fd fenceFd) { if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) { ATRACE_NAME("SkiaGLRenderEngine::waitFence"); sync_wait(fenceFd.get(), -1); } } +base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) { + base::unique_fd drawFence = flush(); + + bool requireSync = drawFence.get() < 0; + if (requireSync) { + ATRACE_BEGIN("Submit(sync=true)"); + } else { + ATRACE_BEGIN("Submit(sync=false)"); + } + bool success = grContext->submit(requireSync); + ATRACE_END(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + // Chances are, something illegal happened (Skia's internal GPU object + // doesn't exist, or the context was abandoned). + return drawFence; + } + + return drawFence; +} + bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { if (!gl::GLExtensions::getInstance().hasNativeFenceSync() || !gl::GLExtensions::getInstance().hasWaitSync()) { @@ -466,960 +377,29 @@ bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { return true; } -static float toDegrees(uint32_t transform) { - switch (transform) { - case ui::Transform::ROT_90: - return 90.0; - case ui::Transform::ROT_180: - return 180.0; - case ui::Transform::ROT_270: - return 270.0; - default: - return 0.0; - } -} - -static SkColorMatrix toSkColorMatrix(const mat4& matrix) { - return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1], - matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2], - matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3], - matrix[3][3], 0); -} - -static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) { - int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK; - int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK; - - // Treat unsupported dataspaces as srgb - if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR && - destTransfer != HAL_DATASPACE_TRANSFER_HLG && - destTransfer != HAL_DATASPACE_TRANSFER_ST2084) { - destTransfer = HAL_DATASPACE_TRANSFER_SRGB; - } - - if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR && - sourceTransfer != HAL_DATASPACE_TRANSFER_HLG && - sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) { - sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB; - } - - const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR; - const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB; - const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR; - const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB; - - return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) && - sourceTransfer != destTransfer; -} - -void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, - bool isRenderable) { - // Only run this if RE is running on its own thread. This way the access to GL - // operations is guaranteed to be happening on the same thread. - if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) { - return; - } - // We currently don't attempt to map a buffer if the buffer contains protected content - // because GPU resources for protected buffers is much more limited. - const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; - if (isProtectedBuffer) { - return; - } - ATRACE_CALL(); - - // If we were to support caching protected buffers then we will need to switch the - // currently bound context if we are not already using the protected context (and subsequently - // switch back after the buffer is cached). However, for non-protected content we can bind - // the texture in either GL context because they are initialized with the same share_context - // which allows the texture state to be shared between them. - auto grContext = getActiveGrContext(); - auto& cache = mTextureCache; - - std::lock_guard<std::mutex> lock(mRenderingMutex); - mGraphicBufferExternalRefs[buffer->getId()]++; - - if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { - std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(grContext, - buffer->toAHardwareBuffer(), - isRenderable, mTextureCleanupMgr); - cache.insert({buffer->getId(), imageTextureRef}); - } -} - -void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { - ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mRenderingMutex); - if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId()); - iter != mGraphicBufferExternalRefs.end()) { - if (iter->second == 0) { - ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64 - "> from RenderEngine texture, but the " - "ref count was already zero!", - buffer->getId()); - mGraphicBufferExternalRefs.erase(buffer->getId()); - return; - } - - iter->second--; - - // Swap contexts if needed prior to deleting this buffer - // See Issue 1 of - // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even - // when a protected context and an unprotected context are part of the same share group, - // protected surfaces may not be accessed by an unprotected context, implying that protected - // surfaces may only be freed when a protected context is active. - const bool inProtected = mInProtectedContext; - useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED); - - if (iter->second == 0) { - mTextureCache.erase(buffer->getId()); - mGraphicBufferExternalRefs.erase(buffer->getId()); - } - - // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger - // are up-to-date. - if (inProtected != mInProtectedContext) { - useProtectedContext(inProtected); - } - } -} - -bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const { - std::lock_guard<std::mutex> lock(mRenderingMutex); - return mTextureCleanupMgr.isEmpty(); -} - -void SkiaGLRenderEngine::cleanupPostRender() { +base::unique_fd SkiaGLRenderEngine::flush() { ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mRenderingMutex); - mTextureCleanupMgr.cleanup(); -} - -// Helper class intended to be used on the stack to ensure that texture cleanup -// is deferred until after this class goes out of scope. -class DeferTextureCleanup final { -public: - DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) { - mMgr.setDeferredStatus(true); - } - ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); } - -private: - DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup); - AutoBackendTexture::CleanupManager& mMgr; -}; - -sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader( - const RuntimeEffectShaderParameters& parameters) { - // The given surface will be stretched by HWUI via matrix transformation - // which gets similar results for most surfaces - // Determine later on if we need to leverage the stertch shader within - // surface flinger - const auto& stretchEffect = parameters.layer.stretchEffect; - auto shader = parameters.shader; - if (stretchEffect.hasEffect()) { - const auto targetBuffer = parameters.layer.source.buffer.buffer; - const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; - if (graphicBuffer && parameters.shader) { - shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); - } - } - - if (parameters.requiresLinearEffect) { - const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace - : ui::Dataspace::V0_SRGB_LINEAR; - const ui::Dataspace outputDataspace = mUseColorManagement - ? parameters.display.outputDataspace - : ui::Dataspace::V0_SRGB_LINEAR; - - auto effect = - shaders::LinearEffect{.inputDataspace = inputDataspace, - .outputDataspace = outputDataspace, - .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha}; - - auto effectIter = mRuntimeEffects.find(effect); - sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; - if (effectIter == mRuntimeEffects.end()) { - runtimeEffect = buildRuntimeEffect(effect); - mRuntimeEffects.insert({effect, runtimeEffect}); - } else { - runtimeEffect = effectIter->second; - } - mat4 colorTransform = parameters.layer.colorTransform; - - colorTransform *= - mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio, - parameters.layerDimmingRatio, 1.f)); - const auto targetBuffer = parameters.layer.source.buffer.buffer; - const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; - const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr; - return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform, - parameters.display.maxLuminance, - parameters.display.currentLuminanceNits, - parameters.layer.source.buffer.maxLuminanceNits, - hardwareBuffer, parameters.display.renderIntent); - } - return parameters.shader; -} - -void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { - if (CC_UNLIKELY(mCapture->isCaptureRunning())) { - // Record display settings when capture is running. - std::stringstream displaySettings; - PrintTo(display, &displaySettings); - // Store the DisplaySettings in additional information. - canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", - SkData::MakeWithCString(displaySettings.str().c_str())); - } - - // Before doing any drawing, let's make sure that we'll start at the origin of the display. - // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual - // displays might have different scaling when compared to the physical screen. - - canvas->clipRect(getSkRect(display.physicalDisplay)); - canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); - - const auto clipWidth = display.clip.width(); - const auto clipHeight = display.clip.height(); - auto rotatedClipWidth = clipWidth; - auto rotatedClipHeight = clipHeight; - // Scale is contingent on the rotation result. - if (display.orientation & ui::Transform::ROT_90) { - std::swap(rotatedClipWidth, rotatedClipHeight); - } - const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / - static_cast<SkScalar>(rotatedClipWidth); - const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / - static_cast<SkScalar>(rotatedClipHeight); - canvas->scale(scaleX, scaleY); - - // Canvas rotation is done by centering the clip window at the origin, rotating, translating - // back so that the top left corner of the clip is at (0, 0). - canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); - canvas->rotate(toDegrees(display.orientation)); - canvas->translate(-clipWidth / 2, -clipHeight / 2); - canvas->translate(-display.clip.left, -display.clip.top); -} - -class AutoSaveRestore { -public: - AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); } - ~AutoSaveRestore() { restore(); } - void replace(SkCanvas* canvas) { - mCanvas = canvas; - mSaveCount = canvas->save(); - } - void restore() { - if (mCanvas) { - mCanvas->restoreToCount(mSaveCount); - mCanvas = nullptr; - } - } - -private: - SkCanvas* mCanvas; - int mSaveCount; -}; - -static SkRRect getBlurRRect(const BlurRegion& region) { - const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); - const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), - SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), - SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), - SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; - SkRRect roundedRect; - roundedRect.setRectRadii(rect, radii); - return roundedRect; -} - -// Arbitrary default margin which should be close enough to zero. -constexpr float kDefaultMargin = 0.0001f; -static bool equalsWithinMargin(float expected, float value, float margin = kDefaultMargin) { - LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!"); - return std::abs(expected - value) < margin; -} - -namespace { -template <typename T> -void logSettings(const T& t) { - std::stringstream stream; - PrintTo(t, &stream); - auto string = stream.str(); - size_t pos = 0; - // Perfetto ignores \n, so split up manually into separate ALOGD statements. - const size_t size = string.size(); - while (pos < size) { - const size_t end = std::min(string.find("\n", pos), size); - ALOGD("%s", string.substr(pos, end - pos).c_str()); - pos = end + 1; - } -} -} // namespace - -void SkiaGLRenderEngine::drawLayersInternal( - const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, - const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/, - base::unique_fd&& bufferFence) { - ATRACE_NAME("SkiaGL::drawLayers"); - - std::lock_guard<std::mutex> lock(mRenderingMutex); - if (layers.empty()) { - ALOGV("Drawing empty layer stack"); - resultPromise->set_value({NO_ERROR, base::unique_fd()}); - return; - } - - if (buffer == nullptr) { - ALOGE("No output buffer provided. Aborting GPU composition."); - resultPromise->set_value({BAD_VALUE, base::unique_fd()}); - return; - } - - validateOutputBufferUsage(buffer->getBuffer()); - - auto grContext = getActiveGrContext(); - auto& cache = mTextureCache; - - // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called - DeferTextureCleanup dtc(mTextureCleanupMgr); - - std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef; - if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) { - surfaceTextureRef = it->second; - } else { - surfaceTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(grContext, - buffer->getBuffer() - ->toAHardwareBuffer(), - true, mTextureCleanupMgr); - } - - // wait on the buffer to be ready to use prior to using it - waitFence(bufferFence); - - const ui::Dataspace dstDataspace = - mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR; - sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext); - - SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); - if (dstCanvas == nullptr) { - ALOGE("Cannot acquire canvas from Skia."); - resultPromise->set_value({BAD_VALUE, base::unique_fd()}); - return; - } - - // setup color filter if necessary - sk_sp<SkColorFilter> displayColorTransform; - if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { - displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); - } - const bool ctModifiesAlpha = - displayColorTransform && !displayColorTransform->isAlphaUnchanged(); - - // Find the max layer white point to determine the max luminance of the scene... - const float maxLayerWhitePoint = std::transform_reduce( - layers.cbegin(), layers.cend(), 0.f, - [](float left, float right) { return std::max(left, right); }, - [&](const auto& l) { return l.whitePointNits; }); - - // ...and compute the dimming ratio if dimming is requested - const float displayDimmingRatio = display.targetLuminanceNits > 0.f && - maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint - ? maxLayerWhitePoint / display.targetLuminanceNits - : 1.f; - - // Find if any layers have requested blur, we'll use that info to decide when to render to an - // offscreen buffer and when to render to the native buffer. - sk_sp<SkSurface> activeSurface(dstSurface); - SkCanvas* canvas = dstCanvas; - SkiaCapture::OffscreenState offscreenCaptureState; - const LayerSettings* blurCompositionLayer = nullptr; - if (mBlurFilter) { - bool requiresCompositionLayer = false; - for (const auto& layer : layers) { - // if the layer doesn't have blur or it is not visible then continue - if (!layerHasBlur(layer, ctModifiesAlpha)) { - continue; - } - if (layer.backgroundBlurRadius > 0 && - layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) { - requiresCompositionLayer = true; - } - for (auto region : layer.blurRegions) { - if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) { - requiresCompositionLayer = true; - } - } - if (requiresCompositionLayer) { - activeSurface = dstSurface->makeSurface(dstSurface->imageInfo()); - canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState); - blurCompositionLayer = &layer; - break; - } - } - } - - AutoSaveRestore surfaceAutoSaveRestore(canvas); - // Clear the entire canvas with a transparent black to prevent ghost images. - canvas->clear(SK_ColorTRANSPARENT); - initCanvas(canvas, display); - - if (kPrintLayerSettings) { - logSettings(display); - } - for (const auto& layer : layers) { - ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str()); - - if (kPrintLayerSettings) { - logSettings(layer); - } - - sk_sp<SkImage> blurInput; - if (blurCompositionLayer == &layer) { - LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); - LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); - - // save a snapshot of the activeSurface to use as input to the blur shaders - blurInput = activeSurface->makeImageSnapshot(); - - // blit the offscreen framebuffer into the destination AHB, but only - // if there are blur regions. backgroundBlurRadius blurs the entire - // image below, so it can skip this step. - if (layer.blurRegions.size()) { - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - if (CC_UNLIKELY(mCapture->isCaptureRunning())) { - uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState); - dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), - String8::format("SurfaceID|%" PRId64, id).c_str(), - nullptr); - dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); - } else { - activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); - } - } - - // assign dstCanvas to canvas and ensure that the canvas state is up to date - canvas = dstCanvas; - surfaceAutoSaveRestore.replace(canvas); - initCanvas(canvas, display); - - LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() != - dstSurface->getCanvas()->getSaveCount()); - LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() != - dstSurface->getCanvas()->getTotalMatrix()); - - // assign dstSurface to activeSurface - activeSurface = dstSurface; - } - - SkAutoCanvasRestore layerAutoSaveRestore(canvas, true); - if (CC_UNLIKELY(mCapture->isCaptureRunning())) { - // Record the name of the layer if the capture is running. - std::stringstream layerSettings; - PrintTo(layer, &layerSettings); - // Store the LayerSettings in additional information. - canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(), - SkData::MakeWithCString(layerSettings.str().c_str())); - } - // Layers have a local transform that should be applied to them - canvas->concat(getSkM44(layer.geometry.positionTransform).asM33()); - - const auto [bounds, roundRectClip] = - getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop, - layer.geometry.roundedCornersRadius); - if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) { - std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; - - // if multiple layers have blur, then we need to take a snapshot now because - // only the lowest layer will have blurImage populated earlier - if (!blurInput) { - blurInput = activeSurface->makeImageSnapshot(); - } - // rect to be blurred in the coordinate space of blurInput - const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); - - // if the clip needs to be applied then apply it now and make sure - // it is restored before we attempt to draw any shadows. - SkAutoCanvasRestore acr(canvas, true); - if (!roundRectClip.isEmpty()) { - canvas->clipRRect(roundRectClip, true); - } - - // TODO(b/182216890): Filter out empty layers earlier - if (blurRect.width() > 0 && blurRect.height() > 0) { - if (layer.backgroundBlurRadius > 0) { - ATRACE_NAME("BackgroundBlur"); - auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius, - blurInput, blurRect); - - cachedBlurs[layer.backgroundBlurRadius] = blurredImage; - - mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f, - blurRect, blurredImage, blurInput); - } - - canvas->concat(getSkM44(layer.blurRegionTransform).asM33()); - for (auto region : layer.blurRegions) { - if (cachedBlurs[region.blurRadius] == nullptr) { - ATRACE_NAME("BlurRegion"); - cachedBlurs[region.blurRadius] = - mBlurFilter->generate(grContext, region.blurRadius, blurInput, - blurRect); - } - - mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, - region.alpha, blurRect, - cachedBlurs[region.blurRadius], blurInput); - } - } - } - - if (layer.shadow.length > 0) { - // This would require a new parameter/flag to SkShadowUtils::DrawShadow - LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow"); - - SkRRect shadowBounds, shadowClip; - if (layer.geometry.boundaries == layer.shadow.boundaries) { - shadowBounds = bounds; - shadowClip = roundRectClip; - } else { - std::tie(shadowBounds, shadowClip) = - getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop, - layer.geometry.roundedCornersRadius); - } - - // Technically, if bounds is a rect and roundRectClip is not empty, - // it means that the bounds and roundedCornersCrop were different - // enough that we should intersect them to find the proper shadow. - // In practice, this often happens when the two rectangles appear to - // not match due to rounding errors. Draw the rounded version, which - // looks more like the intent. - const auto& rrect = - shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds; - drawShadow(canvas, rrect, layer.shadow); - } - - const float layerDimmingRatio = layer.whitePointNits <= 0.f - ? displayDimmingRatio - : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio; - - const bool dimInLinearSpace = display.dimmingStage != - aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; - - const bool requiresLinearEffect = layer.colorTransform != mat4() || - (mUseColorManagement && - needsToneMapping(layer.sourceDataspace, display.outputDataspace)) || - (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)); - - // quick abort from drawing the remaining portion of the layer - if (layer.skipContentDraw || - (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending && - (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) { - continue; - } - - // If we need to map to linear space or color management is disabled, then mark the source - // image with the same colorspace as the destination surface so that Skia's color - // management is a no-op. - const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect) - ? dstDataspace - : layer.sourceDataspace; - - SkPaint paint; - if (layer.source.buffer.buffer) { - ATRACE_NAME("DrawImage"); - validateInputBufferUsage(layer.source.buffer.buffer->getBuffer()); - const auto& item = layer.source.buffer; - std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr; - - if (const auto& iter = cache.find(item.buffer->getBuffer()->getId()); - iter != cache.end()) { - imageTextureRef = iter->second; - } else { - // If we didn't find the image in the cache, then create a local ref but don't cache - // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if - // we didn't find anything in the cache then we intentionally did not cache this - // buffer's resources. - imageTextureRef = std::make_shared< - AutoBackendTexture::LocalRef>(grContext, - item.buffer->getBuffer()->toAHardwareBuffer(), - false, mTextureCleanupMgr); - } - - // if the layer's buffer has a fence, then we must must respect the fence prior to using - // the buffer. - if (layer.source.buffer.fence != nullptr) { - waitFence(layer.source.buffer.fence->get()); - } - - // isOpaque means we need to ignore the alpha in the image, - // replacing it with the alpha specified by the LayerSettings. See - // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) - // The proper way to do this is to use an SkColorType that ignores - // alpha, like kRGB_888x_SkColorType, and that is used if the - // incoming image is kRGBA_8888_SkColorType. However, the incoming - // image may be kRGBA_F16_SkColorType, for which there is no RGBX - // SkColorType, or kRGBA_1010102_SkColorType, for which we have - // kRGB_101010x_SkColorType, but it is not yet supported as a source - // on the GPU. (Adding both is tracked in skbug.com/12048.) In the - // meantime, we'll use a workaround that works unless we need to do - // any color conversion. The workaround requires that we pretend the - // image is already premultiplied, so that we do not premultiply it - // before applying SkBlendMode::kPlus. - const bool useIsOpaqueWorkaround = item.isOpaque && - (imageTextureRef->colorType() == kRGBA_1010102_SkColorType || - imageTextureRef->colorType() == kRGBA_F16_SkColorType); - const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType - : item.isOpaque ? kOpaque_SkAlphaType - : item.usePremultipliedAlpha ? kPremul_SkAlphaType - : kUnpremul_SkAlphaType; - sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext); - - auto texMatrix = getSkM44(item.textureTransform).asM33(); - // textureTansform was intended to be passed directly into a shader, so when - // building the total matrix with the textureTransform we need to first - // normalize it, then apply the textureTransform, then scale back up. - texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height()); - texMatrix.postScale(image->width(), image->height()); - - SkMatrix matrix; - if (!texMatrix.invert(&matrix)) { - matrix = texMatrix; - } - // The shader does not respect the translation, so we add it to the texture - // transform for the SkImage. This will make sure that the correct layer contents - // are drawn in the correct part of the screen. - matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); - - sk_sp<SkShader> shader; - - if (layer.source.buffer.useTextureFiltering) { - shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, - SkSamplingOptions( - {SkFilterMode::kLinear, SkMipmapMode::kNone}), - &matrix); - } else { - shader = image->makeShader(SkSamplingOptions(), matrix); - } - - if (useIsOpaqueWorkaround) { - shader = SkShaders::Blend(SkBlendMode::kPlus, shader, - SkShaders::Color(SkColors::kBlack, - toSkColorSpace(layerDataspace))); - } - - paint.setShader(createRuntimeEffectShader( - RuntimeEffectShaderParameters{.shader = shader, - .layer = layer, - .display = display, - .undoPremultipliedAlpha = !item.isOpaque && - item.usePremultipliedAlpha, - .requiresLinearEffect = requiresLinearEffect, - .layerDimmingRatio = dimInLinearSpace - ? layerDimmingRatio - : 1.f})); - - // Turn on dithering when dimming beyond this (arbitrary) threshold... - static constexpr float kDimmingThreshold = 0.2f; - // ...or we're rendering an HDR layer down to an 8-bit target - // Most HDR standards require at least 10-bits of color depth for source content, so we - // can just extract the transfer function rather than dig into precise gralloc layout. - // Furthermore, we can assume that the only 8-bit target we support is RGBA8888. - const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) && - buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888; - if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) { - paint.setDither(true); - } - paint.setAlphaf(layer.alpha); - - if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { - LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); - - // SysUI creates the alpha layer as a coverage layer, which is - // appropriate for the DPU. Use a color matrix to convert it to - // a mask. - // TODO (b/219525258): Handle input as a mask. - // - // The color matrix will convert A8 pixels with no alpha to - // black, as described by this vector. If the display handles - // the color transform, we need to invert it to find the color - // that will result in black after the DPU applies the transform. - SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a - if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) { - SkM44 colorSpaceMatrix = getSkM44(display.colorTransform); - if (colorSpaceMatrix.invert(&colorSpaceMatrix)) { - black = colorSpaceMatrix * black; - } else { - // We'll just have to use 0,0,0 as black, which should - // be close to correct. - ALOGI("Could not invert colorTransform!"); - } - } - SkColorMatrix colorMatrix(0, 0, 0, 0, black[0], - 0, 0, 0, 0, black[1], - 0, 0, 0, 0, black[2], - 0, 0, 0, -1, 1); - if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { - // On the other hand, if the device doesn't handle it, we - // have to apply it ourselves. - colorMatrix.postConcat(toSkColorMatrix(display.colorTransform)); - } - paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); - } - } else { - ATRACE_NAME("DrawColor"); - const auto color = layer.source.solidColor; - sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, - .fG = color.g, - .fB = color.b, - .fA = layer.alpha}, - toSkColorSpace(layerDataspace)); - paint.setShader(createRuntimeEffectShader( - RuntimeEffectShaderParameters{.shader = shader, - .layer = layer, - .display = display, - .undoPremultipliedAlpha = false, - .requiresLinearEffect = requiresLinearEffect, - .layerDimmingRatio = layerDimmingRatio})); - } - - if (layer.disableBlending) { - paint.setBlendMode(SkBlendMode::kSrc); - } - - // An A8 buffer will already have the proper color filter attached to - // its paint, including the displayColorTransform as needed. - if (!paint.getColorFilter()) { - if (!dimInLinearSpace && !equalsWithinMargin(1.0, layerDimmingRatio)) { - // If we don't dim in linear space, then when we gamma correct the dimming ratio we - // can assume a gamma 2.2 transfer function. - static constexpr float kInverseGamma22 = 1.f / 2.2f; - const auto gammaCorrectedDimmingRatio = - std::pow(layerDimmingRatio, kInverseGamma22); - auto dimmingMatrix = - mat4::scale(vec4(gammaCorrectedDimmingRatio, gammaCorrectedDimmingRatio, - gammaCorrectedDimmingRatio, 1.f)); - - const auto colorFilter = - SkColorFilters::Matrix(toSkColorMatrix(std::move(dimmingMatrix))); - paint.setColorFilter(displayColorTransform - ? displayColorTransform->makeComposed(colorFilter) - : colorFilter); - } else { - paint.setColorFilter(displayColorTransform); - } - } - - if (!roundRectClip.isEmpty()) { - canvas->clipRRect(roundRectClip, true); - } - - if (!bounds.isRect()) { - paint.setAntiAlias(true); - canvas->drawRRect(bounds, paint); - } else { - canvas->drawRect(bounds.rect(), paint); - } - if (kFlushAfterEveryLayer) { - ATRACE_NAME("flush surface"); - activeSurface->flush(); - } - } - surfaceAutoSaveRestore.restore(); - mCapture->endCapture(); - { - ATRACE_NAME("flush surface"); - LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); - activeSurface->flush(); - } - - base::unique_fd drawFence = flush(); - - // If flush failed or we don't support native fences, we need to force the - // gl command stream to be executed. - bool requireSync = drawFence.get() < 0; - if (requireSync) { - ATRACE_BEGIN("Submit(sync=true)"); - } else { - ATRACE_BEGIN("Submit(sync=false)"); - } - bool success = grContext->submit(requireSync); - ATRACE_END(); - if (!success) { - ALOGE("Failed to flush RenderEngine commands"); - // Chances are, something illegal happened (either the caller passed - // us bad parameters, or we messed up our shader generation). - resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)}); - return; - } - - // checkErrors(); - resultPromise->set_value({NO_ERROR, std::move(drawFence)}); - return; -} - -inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) { - return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); -} - -inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { - return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); -} - -/** - * Verifies that common, simple bounds + clip combinations can be converted into - * a single RRect draw call returning true if possible. If true the radii parameter - * will be filled with the correct radii values that combined with bounds param will - * produce the insected roundRect. If false, the returned state of the radii param is undefined. - */ -static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, - const SkRect& insetCrop, const vec2& cornerRadius, - SkVector radii[4]) { - const bool leftEqual = bounds.fLeft == crop.fLeft; - const bool topEqual = bounds.fTop == crop.fTop; - const bool rightEqual = bounds.fRight == crop.fRight; - const bool bottomEqual = bounds.fBottom == crop.fBottom; - - // In the event that the corners of the bounds only partially align with the crop we - // need to ensure that the resulting shape can still be represented as a round rect. - // In particular the round rect implementation will scale the value of all corner radii - // if the sum of the radius along any edge is greater than the length of that edge. - // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap - const bool requiredWidth = bounds.width() > (cornerRadius.x * 2); - const bool requiredHeight = bounds.height() > (cornerRadius.y * 2); - if (!requiredWidth || !requiredHeight) { - return false; - } - - // Check each cropped corner to ensure that it exactly matches the crop or its corner is - // contained within the cropped shape and does not need rounded. - // compute the UpperLeft corner radius - if (leftEqual && topEqual) { - radii[0].set(cornerRadius.x, cornerRadius.y); - } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || - (topEqual && bounds.fLeft >= insetCrop.fLeft)) { - radii[0].set(0, 0); - } else { - return false; - } - // compute the UpperRight corner radius - if (rightEqual && topEqual) { - radii[1].set(cornerRadius.x, cornerRadius.y); - } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || - (topEqual && bounds.fRight <= insetCrop.fRight)) { - radii[1].set(0, 0); - } else { - return false; - } - // compute the BottomRight corner radius - if (rightEqual && bottomEqual) { - radii[2].set(cornerRadius.x, cornerRadius.y); - } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || - (bottomEqual && bounds.fRight <= insetCrop.fRight)) { - radii[2].set(0, 0); - } else { - return false; - } - // compute the BottomLeft corner radius - if (leftEqual && bottomEqual) { - radii[3].set(cornerRadius.x, cornerRadius.y); - } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || - (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { - radii[3].set(0, 0); - } else { - return false; + if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); } - return true; -} - -inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, - const FloatRect& cropRect, - const vec2& cornerRadius) { - const SkRect bounds = getSkRect(boundsRect); - const SkRect crop = getSkRect(cropRect); - - SkRRect clip; - if (cornerRadius.x > 0 && cornerRadius.y > 0) { - // it the crop and the bounds are equivalent or there is no crop then we don't need a clip - if (bounds == crop || crop.isEmpty()) { - return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip}; - } - - // This makes an effort to speed up common, simple bounds + clip combinations by - // converting them to a single RRect draw. It is possible there are other cases - // that can be converted. - if (crop.contains(bounds)) { - const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y); - if (insetCrop.contains(bounds)) { - return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required - } - - SkVector radii[4]; - if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { - SkRRect intersectionBounds; - intersectionBounds.setRectRadii(bounds, radii); - return {intersectionBounds, clip}; - } - } - - // we didn't hit any of our fast paths so set the clip to the cropRect - clip.setRectXY(crop, cornerRadius.x, cornerRadius.y); + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); } - // if we hit this point then we either don't have rounded corners or we are going to rely - // on the clip to round the corners for us - return {SkRRect::MakeRect(bounds), clip}; -} + // native fence fd will not be populated until flush() is done. + glFlush(); -inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings& layer, - bool colorTransformModifiesAlpha) { - if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) { - // return false if the content is opaque and would therefore occlude the blur - const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque; - const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha; - return layer.skipContentDraw || !(opaqueContent && opaqueAlpha); + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); } - return false; -} - -inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) { - return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); -} - -inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) { - return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], - matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], - matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], - matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]); -} -inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) { - return SkPoint3::Make(vector.x, vector.y, vector.z); -} - -size_t SkiaGLRenderEngine::getMaxTextureSize() const { - return mGrContext->maxTextureSize(); -} - -size_t SkiaGLRenderEngine::getMaxViewportDims() const { - return mGrContext->maxRenderTargetSize(); -} - -void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, - const ShadowSettings& settings) { - ATRACE_CALL(); - const float casterZ = settings.length / 2.0f; - const auto flags = - settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; - - SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ), - getSkPoint3(settings.lightPos), settings.lightRadius, - getSkColor(settings.ambientColor), getSkColor(settings.spotColor), - flags); + return fenceFd; } EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, @@ -1539,114 +519,14 @@ int SkiaGLRenderEngine::getContextPriority() { return value; } -void SkiaGLRenderEngine::onActiveDisplaySizeChanged(ui::Size size) { - // This cache multiplier was selected based on review of cache sizes relative - // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x), - // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a - // conservative default based on that analysis. - const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat); - const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER; - - // start by resizing the current context - getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); - - // if it is possible to switch contexts then we will resize the other context - const bool originalProtectedState = mInProtectedContext; - useProtectedContext(!mInProtectedContext); - if (mInProtectedContext != originalProtectedState) { - getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); - // reset back to the initial context that was active when this method was called - useProtectedContext(originalProtectedState); - } -} - -void SkiaGLRenderEngine::dump(std::string& result) { +void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { const gl::GLExtensions& extensions = gl::GLExtensions::getInstance(); - - StringAppendF(&result, "\n ------------RE-----------------\n"); + StringAppendF(&result, "\n ------------RE GLES------------\n"); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), extensions.getVersion()); StringAppendF(&result, "%s\n", extensions.getExtensions()); - StringAppendF(&result, "RenderEngine supports protected context: %d\n", - supportsProtectedContent()); - StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); - StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", - mSkSLCacheMonitor.shadersCachedSinceLastCall()); - - std::vector<ResourcePair> cpuResourceMap = { - {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, - {"skia/sk_resource_cache/rrect-blur_", "Masks"}, - {"skia/sk_resource_cache/rects-blur_", "Masks"}, - {"skia/sk_resource_cache/tessellated", "Shadows"}, - {"skia", "Other"}, - }; - SkiaMemoryReporter cpuReporter(cpuResourceMap, false); - SkGraphics::DumpMemoryStatistics(&cpuReporter); - StringAppendF(&result, "Skia CPU Caches: "); - cpuReporter.logTotals(result); - cpuReporter.logOutput(result); - - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - - std::vector<ResourcePair> gpuResourceMap = { - {"texture_renderbuffer", "Texture/RenderBuffer"}, - {"texture", "Texture"}, - {"gr_text_blob_cache", "Text"}, - {"skia", "Other"}, - }; - SkiaMemoryReporter gpuReporter(gpuResourceMap, true); - mGrContext->dumpMemoryStatistics(&gpuReporter); - StringAppendF(&result, "Skia's GPU Caches: "); - gpuReporter.logTotals(result); - gpuReporter.logOutput(result); - StringAppendF(&result, "Skia's Wrapped Objects:\n"); - gpuReporter.logOutput(result, true); - - StringAppendF(&result, "RenderEngine tracked buffers: %zu\n", - mGraphicBufferExternalRefs.size()); - StringAppendF(&result, "Dumping buffer ids...\n"); - for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) { - StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts); - } - StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n", - mTextureCache.size()); - StringAppendF(&result, "Dumping buffer ids...\n"); - // TODO(178539829): It would be nice to know which layer these are coming from and what - // the texture sizes are. - for (const auto& [id, unused] : mTextureCache) { - StringAppendF(&result, "- 0x%" PRIx64 "\n", id); - } - StringAppendF(&result, "\n"); - - SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); - if (mProtectedGrContext) { - mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); - } - StringAppendF(&result, "Skia's GPU Protected Caches: "); - gpuProtectedReporter.logTotals(result); - gpuProtectedReporter.logOutput(result); - StringAppendF(&result, "Skia's Protected Wrapped Objects:\n"); - gpuProtectedReporter.logOutput(result, true); - - StringAppendF(&result, "\n"); - StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size()); - for (const auto& [linearEffect, unused] : mRuntimeEffects) { - StringAppendF(&result, "- inputDataspace: %s\n", - dataspaceDetails( - static_cast<android_dataspace>(linearEffect.inputDataspace)) - .c_str()); - StringAppendF(&result, "- outputDataspace: %s\n", - dataspaceDetails( - static_cast<android_dataspace>(linearEffect.outputDataspace)) - .c_str()); - StringAppendF(&result, "undoPremultipliedAlpha: %s\n", - linearEffect.undoPremultipliedAlpha ? "true" : "false"); - } - } - StringAppendF(&result, "\n"); } } // namespace skia diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 68c336327b..af3311041d 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -41,6 +41,10 @@ #include "filters/LinearEffect.h" #include "filters/StretchShaderFactory.h" +class SkData; + +struct SkPoint3; + namespace android { namespace renderengine { namespace skia { @@ -48,36 +52,26 @@ namespace skia { class SkiaGLRenderEngine : public skia::SkiaRenderEngine { public: static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, - EGLSurface placeholder, EGLContext protectedContext, - EGLSurface protectedPlaceholder); - ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex); + ~SkiaGLRenderEngine() override; - std::future<void> primeCache() override; - void cleanupPostRender() override; - void cleanFramebufferCache() override{}; int getContextPriority() override; - bool isProtected() const override { return mInProtectedContext; } - bool supportsProtectedContent() const override; - void useProtectedContext(bool useProtectedContext) override; - bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } - void onActiveDisplaySizeChanged(ui::Size size) override; - int reportShadersCompiled() override; protected: - void dump(std::string& result) override; - size_t getMaxTextureSize() const override; - size_t getMaxViewportDims() const override; - void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; - void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; - bool canSkipPostRenderCleanup() const override; - void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, - const DisplaySettings& display, - const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) override; + // Implementations of abstract SkiaRenderEngine functions specific to + // rendering backend + virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options); + bool supportsProtectedContentImpl() const override; + bool useProtectedContextImpl(GrProtected isProtected) override; + void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(GrDirectContext* context) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: + SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, + EGLSurface placeholder, EGLContext protectedContext, + EGLSurface protectedPlaceholder); + bool waitGpuFence(base::borrowed_fd fenceFd); + base::unique_fd flush(); static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); static EGLContext createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, @@ -85,107 +79,14 @@ private: Protection protection); static std::optional<RenderEngine::ContextPriority> createContextPriority( const RenderEngineCreationArgs& args); - static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config, - int hwcFormat, Protection protection); - inline SkRect getSkRect(const FloatRect& layer); - inline SkRect getSkRect(const Rect& layer); - inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds, - const FloatRect& crop, - const vec2& cornerRadius); - inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha); - inline SkColor getSkColor(const vec4& color); - inline SkM44 getSkM44(const mat4& matrix); - inline SkPoint3 getSkPoint3(const vec3& vector); - inline GrDirectContext* getActiveGrContext() const; - - base::unique_fd flush(); - // waitFence attempts to wait in the GPU, and if unable to waits on the CPU instead. - void waitFence(base::borrowed_fd fenceFd); - bool waitGpuFence(base::borrowed_fd fenceFd); - - void initCanvas(SkCanvas* canvas, const DisplaySettings& display); - void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, - const ShadowSettings& shadowSettings); - - // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned. - // Otherwise it returns the input shader. - struct RuntimeEffectShaderParameters { - sk_sp<SkShader> shader; - const LayerSettings& layer; - const DisplaySettings& display; - bool undoPremultipliedAlpha; - bool requiresLinearEffect; - float layerDimmingRatio; - }; - sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); + static EGLSurface createPlaceholderEglPbufferSurface( + EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection); EGLDisplay mEGLDisplay; EGLContext mEGLContext; EGLSurface mPlaceholderSurface; EGLContext mProtectedEGLContext; EGLSurface mProtectedPlaceholderSurface; - BlurFilter* mBlurFilter = nullptr; - - const PixelFormat mDefaultPixelFormat; - const bool mUseColorManagement; - - // Identifier used or various mappings of layers to various - // textures or shaders - using GraphicBufferId = uint64_t; - - // Number of external holders of ExternalTexture references, per GraphicBuffer ID. - std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs - GUARDED_BY(mRenderingMutex); - // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts. - std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache - GUARDED_BY(mRenderingMutex); - std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher> - mRuntimeEffects; - AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex); - - StretchShaderFactory mStretchShaderFactory; - // 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. - mutable std::mutex mRenderingMutex; - - sp<Fence> mLastDrawFence; - - // Graphics context used for creating surfaces and submitting commands - sk_sp<GrDirectContext> mGrContext; - // Same as above, but for protected content (eg. DRM) - sk_sp<GrDirectContext> mProtectedGrContext; - - bool mInProtectedContext = false; - // Object to capture commands send to Skia. - std::unique_ptr<SkiaCapture> mCapture; - - // Implements PersistentCache as a way to monitor what SkSL shaders Skia has - // cached. - class SkSLCacheMonitor : public GrContextOptions::PersistentCache { - public: - SkSLCacheMonitor() = default; - ~SkSLCacheMonitor() override = default; - - sk_sp<SkData> load(const SkData& key) override; - - void store(const SkData& key, const SkData& data, const SkString& description) override; - - int shadersCachedSinceLastCall() { - const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall; - mShadersCachedSinceLastCall = 0; - return shadersCachedSinceLastCall; - } - - int totalShadersCompiled() const { return mTotalShadersCompiled; } - - private: - int mShadersCachedSinceLastCall = 0; - int mTotalShadersCompiled = 0; - }; - - SkSLCacheMonitor mSkSLCacheMonitor; }; } // namespace skia diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 1fb24f5041..413811ef99 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -20,16 +20,1245 @@ #include "SkiaRenderEngine.h" +#include <GrBackendSemaphore.h> +#include <GrContextOptions.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkColorFilter.h> +#include <SkColorMatrix.h> +#include <SkColorSpace.h> +#include <SkData.h> +#include <SkGraphics.h> +#include <SkImage.h> +#include <SkImageFilters.h> +#include <SkImageInfo.h> +#include <SkM44.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkPoint.h> +#include <SkPoint3.h> +#include <SkRect.h> +#include <SkRefCnt.h> +#include <SkRegion.h> +#include <SkRRect.h> +#include <SkRuntimeEffect.h> +#include <SkSamplingOptions.h> +#include <SkScalar.h> +#include <SkShader.h> +#include <SkShadowUtils.h> +#include <SkString.h> +#include <SkSurface.h> +#include <SkTileMode.h> #include <src/core/SkTraceEventCommon.h> +#include <android-base/stringprintf.h> +#include <gui/TraceUtils.h> +#include <sync/sync.h> +#include <ui/BlurRegion.h> +#include <ui/DataspaceUtils.h> +#include <ui/DebugUtils.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> + +#include <cmath> +#include <cstdint> +#include <memory> +#include <numeric> + +#include "Cache.h" +#include "ColorSpaces.h" +#include "filters/BlurFilter.h" +#include "filters/GaussianBlurFilter.h" +#include "filters/KawaseBlurFilter.h" +#include "filters/LinearEffect.h" +#include "log/log_main.h" +#include "skia/debug/SkiaCapture.h" +#include "skia/debug/SkiaMemoryReporter.h" +#include "skia/filters/StretchShaderFactory.h" +#include "system/graphics-base-v1.0.h" + +namespace { + +// Debugging settings +static const bool kPrintLayerSettings = false; +static const bool kFlushAfterEveryLayer = kPrintLayerSettings; + +} // namespace + +// Utility functions related to SkRect + +namespace { + +static inline SkRect getSkRect(const android::FloatRect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +static inline SkRect getSkRect(const android::Rect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +/** + * Verifies that common, simple bounds + clip combinations can be converted into + * a single RRect draw call returning true if possible. If true the radii parameter + * will be filled with the correct radii values that combined with bounds param will + * produce the insected roundRect. If false, the returned state of the radii param is undefined. + */ +static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, + const SkRect& insetCrop, const android::vec2& cornerRadius, + SkVector radii[4]) { + const bool leftEqual = bounds.fLeft == crop.fLeft; + const bool topEqual = bounds.fTop == crop.fTop; + const bool rightEqual = bounds.fRight == crop.fRight; + const bool bottomEqual = bounds.fBottom == crop.fBottom; + + // In the event that the corners of the bounds only partially align with the crop we + // need to ensure that the resulting shape can still be represented as a round rect. + // In particular the round rect implementation will scale the value of all corner radii + // if the sum of the radius along any edge is greater than the length of that edge. + // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap + const bool requiredWidth = bounds.width() > (cornerRadius.x * 2); + const bool requiredHeight = bounds.height() > (cornerRadius.y * 2); + if (!requiredWidth || !requiredHeight) { + return false; + } + + // Check each cropped corner to ensure that it exactly matches the crop or its corner is + // contained within the cropped shape and does not need rounded. + // compute the UpperLeft corner radius + if (leftEqual && topEqual) { + radii[0].set(cornerRadius.x, cornerRadius.y); + } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fLeft >= insetCrop.fLeft)) { + radii[0].set(0, 0); + } else { + return false; + } + // compute the UpperRight corner radius + if (rightEqual && topEqual) { + radii[1].set(cornerRadius.x, cornerRadius.y); + } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fRight <= insetCrop.fRight)) { + radii[1].set(0, 0); + } else { + return false; + } + // compute the BottomRight corner radius + if (rightEqual && bottomEqual) { + radii[2].set(cornerRadius.x, cornerRadius.y); + } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fRight <= insetCrop.fRight)) { + radii[2].set(0, 0); + } else { + return false; + } + // compute the BottomLeft corner radius + if (leftEqual && bottomEqual) { + radii[3].set(cornerRadius.x, cornerRadius.y); + } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { + radii[3].set(0, 0); + } else { + return false; + } + + return true; +} + +static inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const android::FloatRect& boundsRect, + const android::FloatRect& cropRect, + const android::vec2& cornerRadius) { + const SkRect bounds = getSkRect(boundsRect); + const SkRect crop = getSkRect(cropRect); + + SkRRect clip; + if (cornerRadius.x > 0 && cornerRadius.y > 0) { + // it the crop and the bounds are equivalent or there is no crop then we don't need a clip + if (bounds == crop || crop.isEmpty()) { + return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip}; + } + + // This makes an effort to speed up common, simple bounds + clip combinations by + // converting them to a single RRect draw. It is possible there are other cases + // that can be converted. + if (crop.contains(bounds)) { + const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y); + if (insetCrop.contains(bounds)) { + return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required + } + + SkVector radii[4]; + if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { + SkRRect intersectionBounds; + intersectionBounds.setRectRadii(bounds, radii); + return {intersectionBounds, clip}; + } + } + + // we didn't hit any of our fast paths so set the clip to the cropRect + clip.setRectXY(crop, cornerRadius.x, cornerRadius.y); + } + + // if we hit this point then we either don't have rounded corners or we are going to rely + // on the clip to round the corners for us + return {SkRRect::MakeRect(bounds), clip}; +} + +static inline bool layerHasBlur(const android::renderengine::LayerSettings& layer, + bool colorTransformModifiesAlpha) { + if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) { + // return false if the content is opaque and would therefore occlude the blur + const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque; + const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha; + return layer.skipContentDraw || !(opaqueContent && opaqueAlpha); + } + return false; +} + +static inline SkColor getSkColor(const android::vec4& color) { + return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); +} + +static inline SkM44 getSkM44(const android::mat4& matrix) { + return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], + matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], + matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], + matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]); +} + +static inline SkPoint3 getSkPoint3(const android::vec3& vector) { + return SkPoint3::Make(vector.x, vector.y, vector.z); +} + +} // namespace namespace android { namespace renderengine { namespace skia { -SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {} + +using base::StringAppendF; + +std::future<void> SkiaRenderEngine::primeCache() { + Cache::primeShaderCache(this); + return {}; +} + +sk_sp<SkData> SkiaRenderEngine::SkSLCacheMonitor::load(const SkData& key) { + // This "cache" does not actually cache anything. It just allows us to + // monitor Skia's internal cache. So this method always returns null. + return nullptr; +} + +void SkiaRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data, + const SkString& description) { + mShadersCachedSinceLastCall++; + mTotalShadersCompiled++; + ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled); +} + +int SkiaRenderEngine::reportShadersCompiled() { + return mSkSLCacheMonitor.totalShadersCompiled(); +} void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled); } + +SkiaRenderEngine::SkiaRenderEngine( + RenderEngineType type, + PixelFormat pixelFormat, + bool useColorManagement, + bool supportsBackgroundBlur) : + RenderEngine(type), + mDefaultPixelFormat(pixelFormat), + mUseColorManagement(useColorManagement) { + if (supportsBackgroundBlur) { + ALOGD("Background Blurs Enabled"); + mBlurFilter = new KawaseBlurFilter(); + } + mCapture = std::make_unique<SkiaCapture>(); +} + +SkiaRenderEngine::~SkiaRenderEngine() { } + +// To be called from backend dtors. +void SkiaRenderEngine::finishRenderingAndAbandonContext() { + std::lock_guard<std::mutex> lock(mRenderingMutex); + + if (mBlurFilter) { + delete mBlurFilter; + } + + if (mGrContext) { + mGrContext->flushAndSubmit(true); + mGrContext->abandonContext(); + } + + if (mProtectedGrContext) { + mProtectedGrContext->flushAndSubmit(true); + mProtectedGrContext->abandonContext(); + } +} + +void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) { + if (useProtectedContext == mInProtectedContext || + (useProtectedContext && !supportsProtectedContent())) { + return; + } + + // release any scratch resources before switching into a new mode + if (getActiveGrContext()) { + getActiveGrContext()->purgeUnlockedResources(true); + } + + // Backend-specific way to switch to protected context + if (useProtectedContextImpl( + useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) { + mInProtectedContext = useProtectedContext; + // given that we are sharing the same thread between two GrContexts we need to + // make sure that the thread state is reset when switching between the two. + if (getActiveGrContext()) { + getActiveGrContext()->resetContext(); + } + } +} + +GrDirectContext* SkiaRenderEngine::getActiveGrContext() { + return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get(); +} + +static float toDegrees(uint32_t transform) { + switch (transform) { + case ui::Transform::ROT_90: + return 90.0; + case ui::Transform::ROT_180: + return 180.0; + case ui::Transform::ROT_270: + return 270.0; + default: + return 0.0; + } +} + +static SkColorMatrix toSkColorMatrix(const android::mat4& matrix) { + return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1], + matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2], + matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3], + matrix[3][3], 0); +} + +static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) { + int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK; + int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK; + + // Treat unsupported dataspaces as srgb + if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR && + destTransfer != HAL_DATASPACE_TRANSFER_HLG && + destTransfer != HAL_DATASPACE_TRANSFER_ST2084) { + destTransfer = HAL_DATASPACE_TRANSFER_SRGB; + } + + if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR && + sourceTransfer != HAL_DATASPACE_TRANSFER_HLG && + sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) { + sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB; + } + + const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR; + const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB; + const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR; + const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB; + + return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) && + sourceTransfer != destTransfer; +} + +void SkiaRenderEngine::ensureGrContextsCreated() { + if (mGrContext) { + return; + } + + GrContextOptions options; + options.fDisableDriverCorrectnessWorkarounds = true; + options.fDisableDistanceFieldPaths = true; + options.fReducedShaderVariations = true; + options.fPersistentCache = &mSkSLCacheMonitor; + std::tie(mGrContext, mProtectedGrContext) = createDirectContexts(options); +} + +void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, + bool isRenderable) { + // Only run this if RE is running on its own thread. This + // way the access to GL operations is guaranteed to be happening on the + // same thread. + if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && + mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { + return; + } + // We currently don't attempt to map a buffer if the buffer contains protected content + // because GPU resources for protected buffers is much more limited. + const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; + if (isProtectedBuffer) { + return; + } + ATRACE_CALL(); + + // If we were to support caching protected buffers then we will need to switch the + // currently bound context if we are not already using the protected context (and subsequently + // switch back after the buffer is cached). However, for non-protected content we can bind + // the texture in either GL context because they are initialized with the same share_context + // which allows the texture state to be shared between them. + auto grContext = getActiveGrContext(); + auto& cache = mTextureCache; + + std::lock_guard<std::mutex> lock(mRenderingMutex); + mGraphicBufferExternalRefs[buffer->getId()]++; + + if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { + std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = + std::make_shared<AutoBackendTexture::LocalRef>(grContext, + buffer->toAHardwareBuffer(), + isRenderable, mTextureCleanupMgr); + cache.insert({buffer->getId(), imageTextureRef}); + } +} + +void SkiaRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId()); + iter != mGraphicBufferExternalRefs.end()) { + if (iter->second == 0) { + ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64 + "> from RenderEngine texture, but the " + "ref count was already zero!", + buffer->getId()); + mGraphicBufferExternalRefs.erase(buffer->getId()); + return; + } + + iter->second--; + + // Swap contexts if needed prior to deleting this buffer + // See Issue 1 of + // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even + // when a protected context and an unprotected context are part of the same share group, + // protected surfaces may not be accessed by an unprotected context, implying that protected + // surfaces may only be freed when a protected context is active. + const bool inProtected = mInProtectedContext; + useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + + if (iter->second == 0) { + mTextureCache.erase(buffer->getId()); + mGraphicBufferExternalRefs.erase(buffer->getId()); + } + + // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger + // are up-to-date. + if (inProtected != mInProtectedContext) { + useProtectedContext(inProtected); + } + } +} + +bool SkiaRenderEngine::canSkipPostRenderCleanup() const { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return mTextureCleanupMgr.isEmpty(); +} + +void SkiaRenderEngine::cleanupPostRender() { + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mRenderingMutex); + mTextureCleanupMgr.cleanup(); +} + +sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( + const RuntimeEffectShaderParameters& parameters) { + // The given surface will be stretched by HWUI via matrix transformation + // which gets similar results for most surfaces + // Determine later on if we need to leverage the stertch shader within + // surface flinger + const auto& stretchEffect = parameters.layer.stretchEffect; + auto shader = parameters.shader; + if (stretchEffect.hasEffect()) { + const auto targetBuffer = parameters.layer.source.buffer.buffer; + const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; + if (graphicBuffer && parameters.shader) { + shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); + } + } + + if (parameters.requiresLinearEffect) { + const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace + : ui::Dataspace::V0_SRGB_LINEAR; + const ui::Dataspace outputDataspace = mUseColorManagement + ? parameters.display.outputDataspace + : ui::Dataspace::V0_SRGB_LINEAR; + + auto effect = + shaders::LinearEffect{.inputDataspace = inputDataspace, + .outputDataspace = outputDataspace, + .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha}; + + auto effectIter = mRuntimeEffects.find(effect); + sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; + if (effectIter == mRuntimeEffects.end()) { + runtimeEffect = buildRuntimeEffect(effect); + mRuntimeEffects.insert({effect, runtimeEffect}); + } else { + runtimeEffect = effectIter->second; + } + mat4 colorTransform = parameters.layer.colorTransform; + + colorTransform *= + mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio, + parameters.layerDimmingRatio, 1.f)); + const auto targetBuffer = parameters.layer.source.buffer.buffer; + const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; + const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr; + return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform, + parameters.display.maxLuminance, + parameters.display.currentLuminanceNits, + parameters.layer.source.buffer.maxLuminanceNits, + hardwareBuffer, parameters.display.renderIntent); + } + return parameters.shader; +} + +void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + // Record display settings when capture is running. + std::stringstream displaySettings; + PrintTo(display, &displaySettings); + // Store the DisplaySettings in additional information. + canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", + SkData::MakeWithCString(displaySettings.str().c_str())); + } + + // Before doing any drawing, let's make sure that we'll start at the origin of the display. + // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual + // displays might have different scaling when compared to the physical screen. + + canvas->clipRect(getSkRect(display.physicalDisplay)); + canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); + + const auto clipWidth = display.clip.width(); + const auto clipHeight = display.clip.height(); + auto rotatedClipWidth = clipWidth; + auto rotatedClipHeight = clipHeight; + // Scale is contingent on the rotation result. + if (display.orientation & ui::Transform::ROT_90) { + std::swap(rotatedClipWidth, rotatedClipHeight); + } + const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / + static_cast<SkScalar>(rotatedClipWidth); + const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / + static_cast<SkScalar>(rotatedClipHeight); + canvas->scale(scaleX, scaleY); + + // Canvas rotation is done by centering the clip window at the origin, rotating, translating + // back so that the top left corner of the clip is at (0, 0). + canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); + canvas->rotate(toDegrees(display.orientation)); + canvas->translate(-clipWidth / 2, -clipHeight / 2); + canvas->translate(-display.clip.left, -display.clip.top); +} + +class AutoSaveRestore { +public: + AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); } + ~AutoSaveRestore() { restore(); } + void replace(SkCanvas* canvas) { + mCanvas = canvas; + mSaveCount = canvas->save(); + } + void restore() { + if (mCanvas) { + mCanvas->restoreToCount(mSaveCount); + mCanvas = nullptr; + } + } + +private: + SkCanvas* mCanvas; + int mSaveCount; +}; + +static SkRRect getBlurRRect(const BlurRegion& region) { + const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); + const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), + SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), + SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), + SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; + SkRRect roundedRect; + roundedRect.setRectRadii(rect, radii); + return roundedRect; +} + +// Arbitrary default margin which should be close enough to zero. +constexpr float kDefaultMargin = 0.0001f; +static bool equalsWithinMargin(float expected, float value, float margin = kDefaultMargin) { + LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!"); + return std::abs(expected - value) < margin; +} + +namespace { +template <typename T> +void logSettings(const T& t) { + std::stringstream stream; + PrintTo(t, &stream); + auto string = stream.str(); + size_t pos = 0; + // Perfetto ignores \n, so split up manually into separate ALOGD statements. + const size_t size = string.size(); + while (pos < size) { + const size_t end = std::min(string.find("\n", pos), size); + ALOGD("%s", string.substr(pos, end - pos).c_str()); + pos = end + 1; + } +} +} // namespace + +// Helper class intended to be used on the stack to ensure that texture cleanup +// is deferred until after this class goes out of scope. +class DeferTextureCleanup final { +public: + DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) { + mMgr.setDeferredStatus(true); + } + ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); } + +private: + DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup); + AutoBackendTexture::CleanupManager& mMgr; +}; + +void SkiaRenderEngine::drawLayersInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/, + base::unique_fd&& bufferFence) { + ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str()); + + std::lock_guard<std::mutex> lock(mRenderingMutex); + + if (buffer == nullptr) { + ALOGE("No output buffer provided. Aborting GPU composition."); + resultPromise->set_value(base::unexpected(BAD_VALUE)); + return; + } + + validateOutputBufferUsage(buffer->getBuffer()); + + auto grContext = getActiveGrContext(); + auto& cache = mTextureCache; + + // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called + DeferTextureCleanup dtc(mTextureCleanupMgr); + + std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef; + if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) { + surfaceTextureRef = it->second; + } else { + surfaceTextureRef = + std::make_shared<AutoBackendTexture::LocalRef>(grContext, + buffer->getBuffer() + ->toAHardwareBuffer(), + true, mTextureCleanupMgr); + } + + // wait on the buffer to be ready to use prior to using it + waitFence(grContext, bufferFence); + + const ui::Dataspace dstDataspace = + mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR; + sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext); + + SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); + if (dstCanvas == nullptr) { + ALOGE("Cannot acquire canvas from Skia."); + resultPromise->set_value(base::unexpected(BAD_VALUE)); + return; + } + + // setup color filter if necessary + sk_sp<SkColorFilter> displayColorTransform; + if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { + displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); + } + const bool ctModifiesAlpha = + displayColorTransform && !displayColorTransform->isAlphaUnchanged(); + + // Find the max layer white point to determine the max luminance of the scene... + const float maxLayerWhitePoint = std::transform_reduce( + layers.cbegin(), layers.cend(), 0.f, + [](float left, float right) { return std::max(left, right); }, + [&](const auto& l) { return l.whitePointNits; }); + + // ...and compute the dimming ratio if dimming is requested + const float displayDimmingRatio = display.targetLuminanceNits > 0.f && + maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint + ? maxLayerWhitePoint / display.targetLuminanceNits + : 1.f; + + // Find if any layers have requested blur, we'll use that info to decide when to render to an + // offscreen buffer and when to render to the native buffer. + sk_sp<SkSurface> activeSurface(dstSurface); + SkCanvas* canvas = dstCanvas; + SkiaCapture::OffscreenState offscreenCaptureState; + const LayerSettings* blurCompositionLayer = nullptr; + if (mBlurFilter) { + bool requiresCompositionLayer = false; + for (const auto& layer : layers) { + // if the layer doesn't have blur or it is not visible then continue + if (!layerHasBlur(layer, ctModifiesAlpha)) { + continue; + } + if (layer.backgroundBlurRadius > 0 && + layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCompositionLayer = true; + } + for (auto region : layer.blurRegions) { + if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCompositionLayer = true; + } + } + if (requiresCompositionLayer) { + activeSurface = dstSurface->makeSurface(dstSurface->imageInfo()); + canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState); + blurCompositionLayer = &layer; + break; + } + } + } + + AutoSaveRestore surfaceAutoSaveRestore(canvas); + // Clear the entire canvas with a transparent black to prevent ghost images. + canvas->clear(SK_ColorTRANSPARENT); + initCanvas(canvas, display); + + if (kPrintLayerSettings) { + logSettings(display); + } + for (const auto& layer : layers) { + ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str()); + + if (kPrintLayerSettings) { + logSettings(layer); + } + + sk_sp<SkImage> blurInput; + if (blurCompositionLayer == &layer) { + LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); + LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); + + // save a snapshot of the activeSurface to use as input to the blur shaders + blurInput = activeSurface->makeImageSnapshot(); + + // blit the offscreen framebuffer into the destination AHB, but only + // if there are blur regions. backgroundBlurRadius blurs the entire + // image below, so it can skip this step. + if (layer.blurRegions.size()) { + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState); + dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), + String8::format("SurfaceID|%" PRId64, id).c_str(), + nullptr); + dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); + } else { + activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); + } + } + + // assign dstCanvas to canvas and ensure that the canvas state is up to date + canvas = dstCanvas; + surfaceAutoSaveRestore.replace(canvas); + initCanvas(canvas, display); + + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() != + dstSurface->getCanvas()->getSaveCount()); + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() != + dstSurface->getCanvas()->getTotalMatrix()); + + // assign dstSurface to activeSurface + activeSurface = dstSurface; + } + + SkAutoCanvasRestore layerAutoSaveRestore(canvas, true); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + // Record the name of the layer if the capture is running. + std::stringstream layerSettings; + PrintTo(layer, &layerSettings); + // Store the LayerSettings in additional information. + canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(), + SkData::MakeWithCString(layerSettings.str().c_str())); + } + // Layers have a local transform that should be applied to them + canvas->concat(getSkM44(layer.geometry.positionTransform).asM33()); + + const auto [bounds, roundRectClip] = + getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop, + layer.geometry.roundedCornersRadius); + if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) { + std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; + + // if multiple layers have blur, then we need to take a snapshot now because + // only the lowest layer will have blurImage populated earlier + if (!blurInput) { + blurInput = activeSurface->makeImageSnapshot(); + } + // rect to be blurred in the coordinate space of blurInput + const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); + + // if the clip needs to be applied then apply it now and make sure + // it is restored before we attempt to draw any shadows. + SkAutoCanvasRestore acr(canvas, true); + if (!roundRectClip.isEmpty()) { + canvas->clipRRect(roundRectClip, true); + } + + // TODO(b/182216890): Filter out empty layers earlier + if (blurRect.width() > 0 && blurRect.height() > 0) { + if (layer.backgroundBlurRadius > 0) { + ATRACE_NAME("BackgroundBlur"); + auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius, + blurInput, blurRect); + + cachedBlurs[layer.backgroundBlurRadius] = blurredImage; + + mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f, + blurRect, blurredImage, blurInput); + } + + canvas->concat(getSkM44(layer.blurRegionTransform).asM33()); + for (auto region : layer.blurRegions) { + if (cachedBlurs[region.blurRadius] == nullptr) { + ATRACE_NAME("BlurRegion"); + cachedBlurs[region.blurRadius] = + mBlurFilter->generate(grContext, region.blurRadius, blurInput, + blurRect); + } + + mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, + region.alpha, blurRect, + cachedBlurs[region.blurRadius], blurInput); + } + } + } + + if (layer.shadow.length > 0) { + // This would require a new parameter/flag to SkShadowUtils::DrawShadow + LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow"); + + SkRRect shadowBounds, shadowClip; + if (layer.geometry.boundaries == layer.shadow.boundaries) { + shadowBounds = bounds; + shadowClip = roundRectClip; + } else { + std::tie(shadowBounds, shadowClip) = + getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop, + layer.geometry.roundedCornersRadius); + } + + // Technically, if bounds is a rect and roundRectClip is not empty, + // it means that the bounds and roundedCornersCrop were different + // enough that we should intersect them to find the proper shadow. + // In practice, this often happens when the two rectangles appear to + // not match due to rounding errors. Draw the rounded version, which + // looks more like the intent. + const auto& rrect = + shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds; + drawShadow(canvas, rrect, layer.shadow); + } + + const float layerDimmingRatio = layer.whitePointNits <= 0.f + ? displayDimmingRatio + : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio; + + const bool dimInLinearSpace = display.dimmingStage != + aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; + + const bool requiresLinearEffect = layer.colorTransform != mat4() || + (mUseColorManagement && + needsToneMapping(layer.sourceDataspace, display.outputDataspace)) || + (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)); + + // quick abort from drawing the remaining portion of the layer + if (layer.skipContentDraw || + (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending && + (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) { + continue; + } + + // If we need to map to linear space or color management is disabled, then mark the source + // image with the same colorspace as the destination surface so that Skia's color + // management is a no-op. + const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect) + ? dstDataspace + : layer.sourceDataspace; + + SkPaint paint; + if (layer.source.buffer.buffer) { + ATRACE_NAME("DrawImage"); + validateInputBufferUsage(layer.source.buffer.buffer->getBuffer()); + const auto& item = layer.source.buffer; + std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr; + + if (const auto& iter = cache.find(item.buffer->getBuffer()->getId()); + iter != cache.end()) { + imageTextureRef = iter->second; + } else { + // If we didn't find the image in the cache, then create a local ref but don't cache + // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if + // we didn't find anything in the cache then we intentionally did not cache this + // buffer's resources. + imageTextureRef = std::make_shared< + AutoBackendTexture::LocalRef>(grContext, + item.buffer->getBuffer()->toAHardwareBuffer(), + false, mTextureCleanupMgr); + } + + // if the layer's buffer has a fence, then we must must respect the fence prior to using + // the buffer. + if (layer.source.buffer.fence != nullptr) { + waitFence(grContext, layer.source.buffer.fence->get()); + } + + // isOpaque means we need to ignore the alpha in the image, + // replacing it with the alpha specified by the LayerSettings. See + // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) + // The proper way to do this is to use an SkColorType that ignores + // alpha, like kRGB_888x_SkColorType, and that is used if the + // incoming image is kRGBA_8888_SkColorType. However, the incoming + // image may be kRGBA_F16_SkColorType, for which there is no RGBX + // SkColorType, or kRGBA_1010102_SkColorType, for which we have + // kRGB_101010x_SkColorType, but it is not yet supported as a source + // on the GPU. (Adding both is tracked in skbug.com/12048.) In the + // meantime, we'll use a workaround that works unless we need to do + // any color conversion. The workaround requires that we pretend the + // image is already premultiplied, so that we do not premultiply it + // before applying SkBlendMode::kPlus. + const bool useIsOpaqueWorkaround = item.isOpaque && + (imageTextureRef->colorType() == kRGBA_1010102_SkColorType || + imageTextureRef->colorType() == kRGBA_F16_SkColorType); + const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType + : item.isOpaque ? kOpaque_SkAlphaType + : item.usePremultipliedAlpha ? kPremul_SkAlphaType + : kUnpremul_SkAlphaType; + sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext); + + auto texMatrix = getSkM44(item.textureTransform).asM33(); + // textureTansform was intended to be passed directly into a shader, so when + // building the total matrix with the textureTransform we need to first + // normalize it, then apply the textureTransform, then scale back up. + texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height()); + texMatrix.postScale(image->width(), image->height()); + + SkMatrix matrix; + if (!texMatrix.invert(&matrix)) { + matrix = texMatrix; + } + // The shader does not respect the translation, so we add it to the texture + // transform for the SkImage. This will make sure that the correct layer contents + // are drawn in the correct part of the screen. + matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); + + sk_sp<SkShader> shader; + + if (layer.source.buffer.useTextureFiltering) { + shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions( + {SkFilterMode::kLinear, SkMipmapMode::kNone}), + &matrix); + } else { + shader = image->makeShader(SkSamplingOptions(), matrix); + } + + if (useIsOpaqueWorkaround) { + shader = SkShaders::Blend(SkBlendMode::kPlus, shader, + SkShaders::Color(SkColors::kBlack, + toSkColorSpace(layerDataspace))); + } + + paint.setShader(createRuntimeEffectShader( + RuntimeEffectShaderParameters{.shader = shader, + .layer = layer, + .display = display, + .undoPremultipliedAlpha = !item.isOpaque && + item.usePremultipliedAlpha, + .requiresLinearEffect = requiresLinearEffect, + .layerDimmingRatio = dimInLinearSpace + ? layerDimmingRatio + : 1.f})); + + // Turn on dithering when dimming beyond this (arbitrary) threshold... + static constexpr float kDimmingThreshold = 0.2f; + // ...or we're rendering an HDR layer down to an 8-bit target + // Most HDR standards require at least 10-bits of color depth for source content, so we + // can just extract the transfer function rather than dig into precise gralloc layout. + // Furthermore, we can assume that the only 8-bit target we support is RGBA8888. + const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) && + buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888; + if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) { + paint.setDither(true); + } + paint.setAlphaf(layer.alpha); + + if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { + LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); + + // SysUI creates the alpha layer as a coverage layer, which is + // appropriate for the DPU. Use a color matrix to convert it to + // a mask. + // TODO (b/219525258): Handle input as a mask. + // + // The color matrix will convert A8 pixels with no alpha to + // black, as described by this vector. If the display handles + // the color transform, we need to invert it to find the color + // that will result in black after the DPU applies the transform. + SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a + if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) { + SkM44 colorSpaceMatrix = getSkM44(display.colorTransform); + if (colorSpaceMatrix.invert(&colorSpaceMatrix)) { + black = colorSpaceMatrix * black; + } else { + // We'll just have to use 0,0,0 as black, which should + // be close to correct. + ALOGI("Could not invert colorTransform!"); + } + } + SkColorMatrix colorMatrix(0, 0, 0, 0, black[0], + 0, 0, 0, 0, black[1], + 0, 0, 0, 0, black[2], + 0, 0, 0, -1, 1); + if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { + // On the other hand, if the device doesn't handle it, we + // have to apply it ourselves. + colorMatrix.postConcat(toSkColorMatrix(display.colorTransform)); + } + paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); + } + } else { + ATRACE_NAME("DrawColor"); + const auto color = layer.source.solidColor; + sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, + .fG = color.g, + .fB = color.b, + .fA = layer.alpha}, + toSkColorSpace(layerDataspace)); + paint.setShader(createRuntimeEffectShader( + RuntimeEffectShaderParameters{.shader = shader, + .layer = layer, + .display = display, + .undoPremultipliedAlpha = false, + .requiresLinearEffect = requiresLinearEffect, + .layerDimmingRatio = layerDimmingRatio})); + } + + if (layer.disableBlending) { + paint.setBlendMode(SkBlendMode::kSrc); + } + + // An A8 buffer will already have the proper color filter attached to + // its paint, including the displayColorTransform as needed. + if (!paint.getColorFilter()) { + if (!dimInLinearSpace && !equalsWithinMargin(1.0, layerDimmingRatio)) { + // If we don't dim in linear space, then when we gamma correct the dimming ratio we + // can assume a gamma 2.2 transfer function. + static constexpr float kInverseGamma22 = 1.f / 2.2f; + const auto gammaCorrectedDimmingRatio = + std::pow(layerDimmingRatio, kInverseGamma22); + auto dimmingMatrix = + mat4::scale(vec4(gammaCorrectedDimmingRatio, gammaCorrectedDimmingRatio, + gammaCorrectedDimmingRatio, 1.f)); + + const auto colorFilter = + SkColorFilters::Matrix(toSkColorMatrix(std::move(dimmingMatrix))); + paint.setColorFilter(displayColorTransform + ? displayColorTransform->makeComposed(colorFilter) + : colorFilter); + } else { + paint.setColorFilter(displayColorTransform); + } + } + + if (!roundRectClip.isEmpty()) { + canvas->clipRRect(roundRectClip, true); + } + + if (!bounds.isRect()) { + paint.setAntiAlias(true); + canvas->drawRRect(bounds, paint); + } else { + canvas->drawRect(bounds.rect(), paint); + } + if (kFlushAfterEveryLayer) { + ATRACE_NAME("flush surface"); + activeSurface->flush(); + } + } + for (const auto& borderRenderInfo : display.borderInfoList) { + SkPaint p; + p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g, + borderRenderInfo.color.b, borderRenderInfo.color.a}); + p.setAntiAlias(true); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(borderRenderInfo.width); + SkRegion sk_region; + SkPath path; + + // Construct a final SkRegion using Regions + for (const auto& r : borderRenderInfo.combinedRegion) { + sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op); + } + + sk_region.getBoundaryPath(&path); + canvas->drawPath(path, p); + path.close(); + } + + surfaceAutoSaveRestore.restore(); + mCapture->endCapture(); + { + ATRACE_NAME("flush surface"); + LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); + activeSurface->flush(); + } + + base::unique_fd drawFence = flushAndSubmit(grContext); + resultPromise->set_value(sp<Fence>::make(std::move(drawFence))); +} + +size_t SkiaRenderEngine::getMaxTextureSize() const { + return mGrContext->maxTextureSize(); +} + +size_t SkiaRenderEngine::getMaxViewportDims() const { + return mGrContext->maxRenderTargetSize(); +} + +void SkiaRenderEngine::drawShadow(SkCanvas* canvas, + const SkRRect& casterRRect, + const ShadowSettings& settings) { + ATRACE_CALL(); + const float casterZ = settings.length / 2.0f; + const auto flags = + settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; + + SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ), + getSkPoint3(settings.lightPos), settings.lightRadius, + getSkColor(settings.ambientColor), getSkColor(settings.spotColor), + flags); +} + +void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) { + // This cache multiplier was selected based on review of cache sizes relative + // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x), + // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a + // conservative default based on that analysis. + const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat); + const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER; + + // start by resizing the current context + getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); + + // if it is possible to switch contexts then we will resize the other context + const bool originalProtectedState = mInProtectedContext; + useProtectedContext(!mInProtectedContext); + if (mInProtectedContext != originalProtectedState) { + getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); + // reset back to the initial context that was active when this method was called + useProtectedContext(originalProtectedState); + } +} + +void SkiaRenderEngine::dump(std::string& result) { + // Dump for the specific backend (GLES or Vk) + appendBackendSpecificInfoToDump(result); + + // Info about protected content + StringAppendF(&result, "RenderEngine supports protected context: %d\n", + supportsProtectedContent()); + StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); + StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", + mSkSLCacheMonitor.shadersCachedSinceLastCall()); + + std::vector<ResourcePair> cpuResourceMap = { + {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, + {"skia/sk_resource_cache/rrect-blur_", "Masks"}, + {"skia/sk_resource_cache/rects-blur_", "Masks"}, + {"skia/sk_resource_cache/tessellated", "Shadows"}, + {"skia", "Other"}, + }; + SkiaMemoryReporter cpuReporter(cpuResourceMap, false); + SkGraphics::DumpMemoryStatistics(&cpuReporter); + StringAppendF(&result, "Skia CPU Caches: "); + cpuReporter.logTotals(result); + cpuReporter.logOutput(result); + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + + std::vector<ResourcePair> gpuResourceMap = { + {"texture_renderbuffer", "Texture/RenderBuffer"}, + {"texture", "Texture"}, + {"gr_text_blob_cache", "Text"}, + {"skia", "Other"}, + }; + SkiaMemoryReporter gpuReporter(gpuResourceMap, true); + mGrContext->dumpMemoryStatistics(&gpuReporter); + StringAppendF(&result, "Skia's GPU Caches: "); + gpuReporter.logTotals(result); + gpuReporter.logOutput(result); + StringAppendF(&result, "Skia's Wrapped Objects:\n"); + gpuReporter.logOutput(result, true); + + StringAppendF(&result, "RenderEngine tracked buffers: %zu\n", + mGraphicBufferExternalRefs.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) { + StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts); + } + StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n", + mTextureCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + // TODO(178539829): It would be nice to know which layer these are coming from and what + // the texture sizes are. + for (const auto& [id, unused] : mTextureCache) { + StringAppendF(&result, "- 0x%" PRIx64 "\n", id); + } + StringAppendF(&result, "\n"); + + SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); + if (mProtectedGrContext) { + mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); + } + StringAppendF(&result, "Skia's GPU Protected Caches: "); + gpuProtectedReporter.logTotals(result); + gpuProtectedReporter.logOutput(result); + StringAppendF(&result, "Skia's Protected Wrapped Objects:\n"); + gpuProtectedReporter.logOutput(result, true); + + StringAppendF(&result, "\n"); + StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size()); + for (const auto& [linearEffect, unused] : mRuntimeEffects) { + StringAppendF(&result, "- inputDataspace: %s\n", + dataspaceDetails( + static_cast<android_dataspace>(linearEffect.inputDataspace)) + .c_str()); + StringAppendF(&result, "- outputDataspace: %s\n", + dataspaceDetails( + static_cast<android_dataspace>(linearEffect.outputDataspace)) + .c_str()); + StringAppendF(&result, "undoPremultipliedAlpha: %s\n", + linearEffect.undoPremultipliedAlpha ? "true" : "false"); + } + } + StringAppendF(&result, "\n"); +} + } // namespace skia } // namespace renderengine } // namespace android diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 160a18698e..1973c7d065 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -20,6 +20,31 @@ #include <renderengine/RenderEngine.h> #include <sys/types.h> +#include <GrBackendSemaphore.h> +#include <GrDirectContext.h> +#include <SkSurface.h> +#include <android-base/thread_annotations.h> +#include <renderengine/ExternalTexture.h> +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +#include <mutex> +#include <unordered_map> + +#include "AutoBackendTexture.h" +#include "GrContextOptions.h" +#include "SkImageInfo.h" +#include "SkiaRenderEngine.h" +#include "android-base/macros.h" +#include "debug/SkiaCapture.h" +#include "filters/BlurFilter.h" +#include "filters/LinearEffect.h" +#include "filters/StretchShaderFactory.h" + +class SkData; + +struct SkPoint3; + namespace android { namespace renderengine { @@ -31,35 +56,142 @@ namespace skia { class BlurFilter; -// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends -// Currently mostly just handles all the no-op / missing APIs class SkiaRenderEngine : public RenderEngine { public: static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaRenderEngine(RenderEngineType type); - ~SkiaRenderEngine() override {} - - virtual std::future<void> primeCache() override { return {}; }; - virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{}; - virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{}; - virtual bool isProtected() const override { return false; } // mInProtectedContext; } - virtual bool supportsProtectedContent() const override { return false; }; - virtual int getContextPriority() override { return 0; } - virtual int reportShadersCompiled() { return 0; } - virtual void setEnableTracing(bool tracingEnabled) override; + SkiaRenderEngine(RenderEngineType type, + PixelFormat pixelFormat, + bool useColorManagement, + bool supportsBackgroundBlur); + ~SkiaRenderEngine() override; + + std::future<void> primeCache() override final; + void cleanupPostRender() override final; + void cleanFramebufferCache() override final{ } + bool supportsBackgroundBlur() override final { + return mBlurFilter != nullptr; + } + void onActiveDisplaySizeChanged(ui::Size size) override final; + int reportShadersCompiled(); + virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override final{}; + virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override final{}; + virtual void setEnableTracing(bool tracingEnabled) override final; + + void useProtectedContext(bool useProtectedContext) override; + bool supportsProtectedContent() const override { + return supportsProtectedContentImpl(); + } + void ensureGrContextsCreated(); protected: - virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/, - bool /*isRenderable*/) override = 0; - virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0; - - virtual void drawLayersInternal( - const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, - const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) override { - resultPromise->set_value({NO_ERROR, base::unique_fd()}); + // This is so backends can stop the generic rendering state first before + // cleaning up backend-specific state + void finishRenderingAndAbandonContext(); + + // Functions that a given backend (GLES, Vulkan) must implement + using Contexts = std::pair<sk_sp<GrDirectContext>, sk_sp<GrDirectContext>>; + virtual Contexts createDirectContexts(const GrContextOptions& options) = 0; + virtual bool supportsProtectedContentImpl() const = 0; + virtual bool useProtectedContextImpl(GrProtected isProtected) = 0; + virtual void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) = 0; + virtual base::unique_fd flushAndSubmit(GrDirectContext* context) = 0; + virtual void appendBackendSpecificInfoToDump(std::string& result) = 0; + + size_t getMaxTextureSize() const override final; + size_t getMaxViewportDims() const override final; + GrDirectContext* getActiveGrContext(); + + bool isProtected() const { return mInProtectedContext; } + + // Implements PersistentCache as a way to monitor what SkSL shaders Skia has + // cached. + class SkSLCacheMonitor : public GrContextOptions::PersistentCache { + public: + SkSLCacheMonitor() = default; + ~SkSLCacheMonitor() override = default; + + sk_sp<SkData> load(const SkData& key) override; + + void store(const SkData& key, const SkData& data, const SkString& description) override; + + int shadersCachedSinceLastCall() { + const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall; + mShadersCachedSinceLastCall = 0; + return shadersCachedSinceLastCall; + } + + int totalShadersCompiled() const { return mTotalShadersCompiled; } + + private: + int mShadersCachedSinceLastCall = 0; + int mTotalShadersCompiled = 0; + }; + +private: + void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, + bool isRenderable) override final; + void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override final; + bool canSkipPostRenderCleanup() const override final; + + void initCanvas(SkCanvas* canvas, const DisplaySettings& display); + void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, + const ShadowSettings& shadowSettings); + void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence) override final; + + void dump(std::string& result) override final; + + // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned. + // Otherwise it returns the input shader. + struct RuntimeEffectShaderParameters { + sk_sp<SkShader> shader; + const LayerSettings& layer; + const DisplaySettings& display; + bool undoPremultipliedAlpha; + bool requiresLinearEffect; + float layerDimmingRatio; }; + sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); + + const PixelFormat mDefaultPixelFormat; + const bool mUseColorManagement; + + // Identifier used for various mappings of layers to various + // textures or shaders + using GraphicBufferId = uint64_t; + + // Number of external holders of ExternalTexture references, per GraphicBuffer ID. + std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs + GUARDED_BY(mRenderingMutex); + // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts. + std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache + GUARDED_BY(mRenderingMutex); + std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher> + mRuntimeEffects; + AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex); + + StretchShaderFactory mStretchShaderFactory; + + sp<Fence> mLastDrawFence; + BlurFilter* mBlurFilter = nullptr; + + // Object to capture commands send to Skia. + std::unique_ptr<SkiaCapture> mCapture; + + // Mutex guarding rendering operations, so that internal state related to + // rendering that is potentially modified by multiple threads is guaranteed thread-safe. + mutable std::mutex mRenderingMutex; + SkSLCacheMonitor mSkSLCacheMonitor; + + // Graphics context used for creating surfaces and submitting commands + sk_sp<GrDirectContext> mGrContext; + // Same as above, but for protected content (eg. DRM) + sk_sp<GrDirectContext> mProtectedGrContext; + bool mInProtectedContext = false; }; } // namespace skia diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp new file mode 100644 index 0000000000..2b8495c3f7 --- /dev/null +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -0,0 +1,680 @@ +/* + * Copyright 2022 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. + */ + +// Allow the SkiaVkRenderEngine class to not be compiled, to save space +// NOTE: In order to build this class, define `RE_SKIAVK` in a build file. +#ifdef RE_SKIAVK + +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "SkiaVkRenderEngine.h" + +#include <GrBackendSemaphore.h> +#include <GrContextOptions.h> +#include <vk/GrVkExtensions.h> +#include <vk/GrVkTypes.h> + +#include <android-base/stringprintf.h> +#include <gui/TraceUtils.h> +#include <sync/sync.h> +#include <utils/Trace.h> + +#include <cstdint> +#include <memory> +#include <vector> + +#include <vulkan/vulkan.h> +#include "log/log_main.h" + +namespace android { +namespace renderengine { + +struct VulkanFuncs { + PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; + PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; + + PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkDestroyInstance vkDestroyInstance = nullptr; +}; + +struct VulkanInterface { + bool initialized = false; + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkDevice device; + VkQueue queue; + int queueIndex; + uint32_t apiVersion; + GrVkExtensions grExtensions; + VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; + VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; + VkPhysicalDeviceProtectedMemoryProperties* protectedMemoryFeatures = nullptr; + GrVkGetProc grGetProc; + bool isProtected; + bool isRealtimePriority; + + VulkanFuncs funcs; + + std::vector<std::string> instanceExtensionNames; + std::vector<std::string> deviceExtensionNames; + + GrVkBackendContext getBackendContext() { + GrVkBackendContext backendContext; + backendContext.fInstance = instance; + backendContext.fPhysicalDevice = physicalDevice; + backendContext.fDevice = device; + backendContext.fQueue = queue; + backendContext.fGraphicsQueueIndex = queueIndex; + backendContext.fMaxAPIVersion = apiVersion; + backendContext.fVkExtensions = &grExtensions; + backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; + backendContext.fGetProc = grGetProc; + backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; + return backendContext; + }; + + VkSemaphore createExportableSemaphore() { + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = &exportInfo; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); + return VK_NULL_HANDLE; + } + + return semaphore; + } + + // syncFd cannot be <= 0 + VkSemaphore importSemaphoreFromSyncFd(int syncFd) { + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create import semaphore", __func__); + return VK_NULL_HANDLE; + } + + VkImportSemaphoreFdInfoKHR importInfo; + importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importInfo.pNext = nullptr; + importInfo.semaphore = semaphore; + importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; + importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + importInfo.fd = syncFd; + + err = funcs.vkImportSemaphoreFdKHR(device, &importInfo); + if (VK_SUCCESS != err) { + funcs.vkDestroySemaphore(device, semaphore, nullptr); + ALOGE("%s: failed to import semaphore", __func__); + return VK_NULL_HANDLE; + } + + return semaphore; + } + + int exportSemaphoreSyncFd(VkSemaphore semaphore) { + int res; + + VkSemaphoreGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.semaphore = semaphore; + getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to export semaphore, err: %d", __func__, err); + return -1; + } + return res; + } + + void destroySemaphore(VkSemaphore semaphore) { + funcs.vkDestroySemaphore(device, semaphore, nullptr); + } +}; + +static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); +}; + +#define BAIL(fmt, ...) \ + { \ + ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ + return interface; \ + } + +#define CHECK_NONNULL(expr) \ + if ((expr) == nullptr) { \ + BAIL("[%s] null", #expr); \ + } + +#define VK_CHECK(expr) \ + if ((expr) != VK_SUCCESS) { \ + BAIL("[%s] failed. err = %d", #expr, expr); \ + return interface; \ + } + +#define VK_GET_PROC(F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_INST_PROC(instance, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_DEV_PROC(device, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ + CHECK_NONNULL(vk##F) + +VulkanInterface initVulkanInterface(bool protectedContent = false) { + VulkanInterface interface; + + VK_GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion; + VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); + + if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { + return interface; + } + + const VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, + VK_MAKE_VERSION(1, 1, 0), + }; + + VK_GET_PROC(EnumerateInstanceExtensionProperties); + + uint32_t extensionCount = 0; + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); + std::vector<VkExtensionProperties> instanceExtensions(extensionCount); + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, + instanceExtensions.data())); + std::vector<const char*> enabledInstanceExtensionNames; + enabledInstanceExtensionNames.reserve(instanceExtensions.size()); + interface.instanceExtensionNames.reserve(instanceExtensions.size()); + for (const auto& instExt : instanceExtensions) { + enabledInstanceExtensionNames.push_back(instExt.extensionName); + interface.instanceExtensionNames.push_back(instExt.extensionName); + } + + const VkInstanceCreateInfo instanceCreateInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + 0, + nullptr, + (uint32_t)enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + }; + + VK_GET_PROC(CreateInstance); + VkInstance instance; + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + + VK_GET_INST_PROC(instance, DestroyInstance); + interface.funcs.vkDestroyInstance = vkDestroyInstance; + VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); + VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); + VK_GET_INST_PROC(instance, CreateDevice); + + uint32_t physdevCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); + if (physdevCount == 0) { + BAIL("Could not find any physical devices"); + } + + physdevCount = 1; + VkPhysicalDevice physicalDevice; + VkResult enumeratePhysDevsErr = + vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); + if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { + BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", + enumeratePhysDevsErr); + } + + VkPhysicalDeviceProperties2 physDevProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + 0, + {}, + }; + VkPhysicalDeviceProtectedMemoryProperties protMemProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + 0, + {}, + }; + + if (protectedContent) { + physDevProps.pNext = &protMemProps; + } + + vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); + if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + BAIL("Could not find a Vulkan 1.1+ physical device"); + } + + // Check for syncfd support. Bail if we cannot both import and export them. + VkPhysicalDeviceExternalSemaphoreInfo semInfo = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkExternalSemaphoreProperties semProps = { + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, + }; + vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); + + bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + if (!sufficientSemaphoreSyncFdSupport) { + BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } else { + ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } + + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, nullptr); + if (queueCount == 0) { + BAIL("Could not find queues for physical device"); + } + + std::vector<VkQueueFamilyProperties> queueProps(queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); + + int graphicsQueueIndex = -1; + for (uint32_t i = 0; i < queueCount; ++i) { + if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + graphicsQueueIndex = i; + break; + } + } + + if (graphicsQueueIndex == -1) { + BAIL("Could not find a graphics queue family"); + } + + uint32_t deviceExtensionCount; + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + nullptr)); + std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + deviceExtensions.data())); + + std::vector<const char*> enabledDeviceExtensionNames; + enabledDeviceExtensionNames.reserve(deviceExtensions.size()); + interface.deviceExtensionNames.reserve(deviceExtensions.size()); + for (const auto& devExt : deviceExtensions) { + enabledDeviceExtensionNames.push_back(devExt.extensionName); + interface.deviceExtensionNames.push_back(devExt.extensionName); + } + + interface.grExtensions.init(sGetProc, instance, physicalDevice, + enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); + + if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + BAIL("Vulkan driver doesn't support external semaphore fd"); + } + + interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; + interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + interface.physicalDeviceFeatures2->pNext = nullptr; + + interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; + interface.samplerYcbcrConversionFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + interface.samplerYcbcrConversionFeatures->pNext = nullptr; + + interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures; + void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext; + + if (protectedContent) { + interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryProperties; + interface.protectedMemoryFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; + interface.protectedMemoryFeatures->pNext = nullptr; + *tailPnext = interface.protectedMemoryFeatures; + tailPnext = &interface.protectedMemoryFeatures->pNext; + } + + vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); + // Looks like this would slow things down and we can't depend on it on all platforms + interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; + + float queuePriorities[1] = {0.0f}; + void* queueNextPtr = nullptr; + + VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, + nullptr, + // If queue priority is supported, RE should always have realtime priority. + VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, + }; + + if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + queueNextPtr = &queuePriorityCreateInfo; + interface.isRealtimePriority = true; + } + + VkDeviceQueueCreateFlags deviceQueueCreateFlags = + (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); + + const VkDeviceQueueCreateInfo queueInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueNextPtr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, + 1, + queuePriorities, + }; + + const VkDeviceCreateInfo deviceInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + interface.physicalDeviceFeatures2, + 0, + 1, + &queueInfo, + 0, + nullptr, + (uint32_t)enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data(), + nullptr, + }; + + ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); + VkDevice device; + VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); + ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); + + VkQueue graphicsQueue; + VK_GET_DEV_PROC(device, GetDeviceQueue); + vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue); + + VK_GET_DEV_PROC(device, DeviceWaitIdle); + VK_GET_DEV_PROC(device, DestroyDevice); + interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle; + interface.funcs.vkDestroyDevice = vkDestroyDevice; + + VK_GET_DEV_PROC(device, CreateSemaphore); + VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); + VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); + VK_GET_DEV_PROC(device, DestroySemaphore); + interface.funcs.vkCreateSemaphore = vkCreateSemaphore; + interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; + interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; + interface.funcs.vkDestroySemaphore = vkDestroySemaphore; + + // At this point, everything's succeeded and we can continue + interface.initialized = true; + interface.instance = instance; + interface.physicalDevice = physicalDevice; + interface.device = device; + interface.queue = graphicsQueue; + interface.queueIndex = graphicsQueueIndex; + interface.apiVersion = physDevProps.properties.apiVersion; + // grExtensions already constructed + // feature pointers already constructed + interface.grGetProc = sGetProc; + interface.isProtected = protectedContent; + // funcs already initialized + + ALOGD("%s: Success init Vulkan interface", __func__); + return interface; +} + +void teardownVulkanInterface(VulkanInterface* interface) { + interface->initialized = false; + + if (interface->device != VK_NULL_HANDLE) { + interface->funcs.vkDeviceWaitIdle(interface->device); + interface->funcs.vkDestroyDevice(interface->device, nullptr); + interface->device = VK_NULL_HANDLE; + } + if (interface->instance != VK_NULL_HANDLE) { + interface->funcs.vkDestroyInstance(interface->instance, nullptr); + interface->instance = VK_NULL_HANDLE; + } + + if (interface->protectedMemoryFeatures) { + delete interface->protectedMemoryFeatures; + } + + if (interface->samplerYcbcrConversionFeatures) { + delete interface->samplerYcbcrConversionFeatures; + } + + if (interface->physicalDeviceFeatures2) { + delete interface->physicalDeviceFeatures2; + } + + interface->samplerYcbcrConversionFeatures = nullptr; + interface->physicalDeviceFeatures2 = nullptr; + interface->protectedMemoryFeatures = nullptr; +} + +static VulkanInterface sVulkanInterface; +static VulkanInterface sProtectedContentVulkanInterface; + +static void sSetupVulkanInterface() { + if (!sVulkanInterface.initialized) { + sVulkanInterface = initVulkanInterface(false /* no protected content */); + // We will have to abort if non-protected VkDevice creation fails (then nothing works). + LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized, + "Could not initialize Vulkan RenderEngine!"); + } + if (!sProtectedContentVulkanInterface.initialized) { + sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */); + if (!sProtectedContentVulkanInterface.initialized) { + ALOGE("Could not initialize protected content Vulkan RenderEngine."); + } + } +} + +namespace skia { + +using base::StringAppendF; + +bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() { + VulkanInterface temp = initVulkanInterface(false /* no protected content */); + ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.", + temp.initialized ? "true" : "false"); + return temp.initialized; +} + +std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( + const RenderEngineCreationArgs& args) { + std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); + engine->ensureGrContextsCreated(); + + if (sVulkanInterface.initialized) { + ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__); + return engine; + } else { + ALOGD("SkiaVkRenderEngine::%s: could not create SkiaVkRenderEngine. " + "Likely insufficient Vulkan support", + __func__); + return {}; + } +} + +SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) + : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + args.useColorManagement, args.supportsBackgroundBlur) {} + +SkiaVkRenderEngine::~SkiaVkRenderEngine() { + finishRenderingAndAbandonContext(); +} + +SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts( + const GrContextOptions& options) { + sSetupVulkanInterface(); + + SkiaRenderEngine::Contexts contexts; + contexts.first = GrDirectContext::MakeVulkan(sVulkanInterface.getBackendContext(), options); + if (supportsProtectedContentImpl()) { + contexts.second = + GrDirectContext::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(), + options); + } + + return contexts; +} + +bool SkiaVkRenderEngine::supportsProtectedContentImpl() const { + return sProtectedContentVulkanInterface.initialized; +} + +bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) { + return true; +} + +static void delete_semaphore(void* _semaphore) { + VkSemaphore semaphore = (VkSemaphore)_semaphore; + sVulkanInterface.destroySemaphore(semaphore); +} + +static void delete_semaphore_protected(void* _semaphore) { + VkSemaphore semaphore = (VkSemaphore)_semaphore; + sProtectedContentVulkanInterface.destroySemaphore(semaphore); +} + +static VulkanInterface& getVulkanInterface(bool protectedContext) { + if (protectedContext) { + return sProtectedContentVulkanInterface; + } + return sVulkanInterface; +} + +void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) { + if (fenceFd.get() < 0) return; + + int dupedFd = dup(fenceFd.get()); + if (dupedFd < 0) { + ALOGE("failed to create duplicate fence fd: %d", dupedFd); + sync_wait(fenceFd.get(), -1); + return; + } + + base::unique_fd fenceDup(dupedFd); + VkSemaphore waitSemaphore = + getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); + GrBackendSemaphore beSemaphore; + beSemaphore.initVulkan(waitSemaphore); + grContext->wait(1, &beSemaphore, true /* delete after wait */); +} + +base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { + VkSemaphore signalSemaphore = getVulkanInterface(isProtected()).createExportableSemaphore(); + GrBackendSemaphore beSignalSemaphore; + beSignalSemaphore.initVulkan(signalSemaphore); + GrFlushInfo flushInfo; + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &beSignalSemaphore; + flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore; + flushInfo.fFinishedContext = (void*)signalSemaphore; + GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); + grContext->submit(false /* no cpu sync */); + int drawFenceFd = -1; + if (GrSemaphoresSubmitted::kYes == submitted) { + drawFenceFd = getVulkanInterface(isProtected()).exportSemaphoreSyncFd(signalSemaphore); + } + base::unique_fd res(drawFenceFd); + return res; +} + +int SkiaVkRenderEngine::getContextPriority() { + // EGL_CONTEXT_PRIORITY_REALTIME_NV + constexpr int kRealtimePriority = 0x3357; + if (getVulkanInterface(isProtected()).isRealtimePriority) { + return kRealtimePriority; + } else { + return 0; + } +} + +void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan----------\n"); + StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized); + StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", + sProtectedContentVulkanInterface.initialized); + + if (!sVulkanInterface.initialized) { + return; + } + + StringAppendF(&result, "\n Instance extensions:\n"); + for (const auto& name : sVulkanInterface.instanceExtensionNames) { + StringAppendF(&result, "\n %s\n", name.c_str()); + } + + StringAppendF(&result, "\n Device extensions:\n"); + for (const auto& name : sVulkanInterface.deviceExtensionNames) { + StringAppendF(&result, "\n %s\n", name.c_str()); + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android +#endif // RE_SKIAVK diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h new file mode 100644 index 0000000000..1e42b80c10 --- /dev/null +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -0,0 +1,63 @@ +/* + * Copyright 2022 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 SF_SKIAVKRENDERENGINE_H_ +#define SF_SKIAVKRENDERENGINE_H_ + +// Allow the SkiaVkRenderEngine class to not be compiled, to save space +// NOTE: In order to build this class, define `RE_SKIAVK` in a build file. +#ifdef RE_SKIAVK + +#include <vk/GrVkBackendContext.h> + +#include "SkiaRenderEngine.h" + +namespace android { +namespace renderengine { +namespace skia { + +class SkiaVkRenderEngine : public SkiaRenderEngine { +public: + // Returns false if Vulkan implementation can't support SkiaVkRenderEngine. + static bool canSupportSkiaVkRenderEngine(); + static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args); + ~SkiaVkRenderEngine() override; + + int getContextPriority() override; + +protected: + // Implementations of abstract SkiaRenderEngine functions specific to + // rendering backend + virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options); + bool supportsProtectedContentImpl() const override; + bool useProtectedContextImpl(GrProtected isProtected) override; + void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(GrDirectContext* context) override; + void appendBackendSpecificInfoToDump(std::string& result) override; + +private: + SkiaVkRenderEngine(const RenderEngineCreationArgs& args); + base::unique_fd flush(); + + GrVkBackendContext mBackendContext; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif // RE_SKIAVK +#endif // SF_SKIAVKRENDERENGINE_H_ diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp index 856fff4b01..b21b01cc1b 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.cpp +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -27,6 +27,9 @@ #include <utils/Trace.h> #include "CommonPool.h" +#include "SkCanvas.h" +#include "SkRect.h" +#include "SkTypeface.h" #include "src/utils/SkMultiPictureDocument.h" namespace android { diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h index f1946290ca..d65a579916 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.h +++ b/libs/renderengine/skia/debug/SkiaCapture.h @@ -19,13 +19,15 @@ #include <SkDocument.h> #include <SkNWayCanvas.h> #include <SkPictureRecorder.h> +#include <SkRefCnt.h> +#include <SkStream.h> #include <SkSurface.h> +#include "tools/SkSharingProc.h" #include <chrono> #include <mutex> #include "CaptureTimer.h" -#include "tools/SkSharingProc.h" namespace android { namespace renderengine { diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 63cc02b7ea..2557ac9770 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -17,7 +17,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "BlurFilter.h" #include <SkCanvas.h> -#include <SkData.h> #include <SkPaint.h> #include <SkRRect.h> #include <SkRuntimeEffect.h> diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index 55867a95cc..f3b6ab9885 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -18,7 +18,6 @@ #include "GaussianBlurFilter.h" #include <SkCanvas.h> -#include <SkData.h> #include <SkPaint.h> #include <SkRRect.h> #include <SkRuntimeEffect.h> diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index bfde06fd9a..e370c39a94 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -18,7 +18,6 @@ #include "KawaseBlurFilter.h" #include <SkCanvas.h> -#include <SkData.h> #include <SkPaint.h> #include <SkRRect.h> #include <SkRuntimeEffect.h> diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index bbab792dba..6f328d738c 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -24,6 +24,7 @@ package { cc_test { name: "librenderengine_test", defaults: [ + "android.hardware.graphics.composer3-ndk_shared", "skia_deps", "surfaceflinger_defaults", ], @@ -49,7 +50,6 @@ cc_test { ], shared_libs: [ - "android.hardware.graphics.composer3-V1-ndk", "libbase", "libcutils", "libEGL", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 8889f76ccf..7db95a7ea0 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -37,8 +37,8 @@ #include <condition_variable> #include <fstream> -#include "../gl/GLESRenderEngine.h" #include "../skia/SkiaGLRenderEngine.h" +#include "../skia/SkiaVkRenderEngine.h" #include "../threaded/RenderEngineThreaded.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; @@ -108,25 +108,25 @@ public: virtual std::string name() = 0; virtual renderengine::RenderEngine::RenderEngineType type() = 0; virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; - virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() { - return nullptr; - } + virtual bool typeSupported() = 0; virtual bool useColorManagement() const = 0; }; -class GLESRenderEngineFactory : public RenderEngineFactory { +#ifdef RE_SKIAVK +class SkiaVkRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "GLESRenderEngineFactory"; } + std::string name() override { return "SkiaVkRenderEngineFactory"; } renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::GLES; + return renderengine::RenderEngine::RenderEngineType::SKIA_VK; } std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - return createGLESRenderEngine(); + std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine(); + return re; } - std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() { + std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() { renderengine::RenderEngineCreationArgs reCreationArgs = renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) @@ -139,41 +139,21 @@ public: .setRenderEngineType(type()) .setUseColorManagerment(useColorManagement()) .build(); - return renderengine::gl::GLESRenderEngine::create(reCreationArgs); + return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); } + bool typeSupported() override { + return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); + } bool useColorManagement() const override { return false; } + void skip() { GTEST_SKIP(); } }; -class GLESCMRenderEngineFactory : public RenderEngineFactory { +class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory { public: - std::string name() override { return "GLESCMRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::GLES; - } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - return createGLESRenderEngine(); - } - - std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .setUseColorManagerment(useColorManagement()) - .build(); - return renderengine::gl::GLESRenderEngine::create(reCreationArgs); - } - bool useColorManagement() const override { return true; } }; +#endif // RE_SKIAVK class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: @@ -198,6 +178,7 @@ public: return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } + bool typeSupported() override { return true; } bool useColorManagement() const override { return false; } }; @@ -224,6 +205,7 @@ public: return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } + bool typeSupported() override { return true; } bool useColorManagement() const override { return true; } }; @@ -232,14 +214,14 @@ public: std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() { return std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, - HAL_PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "output"), + ExternalTexture>(sp<GraphicBuffer>:: + make(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), *mRE, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage:: @@ -251,12 +233,12 @@ public: uint32_t height) { return std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(width, height, - HAL_PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_TEXTURE, - "input"), + ExternalTexture>(sp<GraphicBuffer>:: + make(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_TEXTURE, + "input"), *mRE, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage:: @@ -285,10 +267,12 @@ public: } std::shared_ptr<renderengine::ExternalTexture> allocateR8Buffer(int width, int height) { - auto buffer = new GraphicBuffer(width, height, android::PIXEL_FORMAT_R_8, 1, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_TEXTURE, - "r8"); + const auto kUsageFlags = + static_cast<uint64_t>(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_TEXTURE); + auto buffer = + sp<GraphicBuffer>::make(static_cast<uint32_t>(width), static_cast<uint32_t>(height), + android::PIXEL_FORMAT_R_8, 1u, kUsageFlags, "r8"); if (buffer->initCheck() != 0) { // Devices are not required to support R8. return nullptr; @@ -311,9 +295,6 @@ public: } for (uint32_t texName : mTexNames) { mRE->deleteTextures(1, &texName); - if (mGLESRE != nullptr) { - EXPECT_FALSE(mGLESRE->isTextureNameKnownForTesting(texName)); - } } const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); @@ -526,20 +507,15 @@ public: void invokeDraw(const renderengine::DisplaySettings& settings, const std::vector<renderengine::LayerSettings>& layers) { - std::future<renderengine::RenderEngineResult> result = + ftl::Future<FenceResult> future = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); + ASSERT_TRUE(future.valid()); - ASSERT_TRUE(result.valid()); - auto [status, fence] = result.get(); - - ASSERT_EQ(NO_ERROR, status); - if (fence.ok()) { - sync_wait(fence.get(), -1); - } + auto result = future.get(); + ASSERT_TRUE(result.ok()); - if (layers.size() > 0 && mGLESRE != nullptr) { - ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId())); - } + auto fence = result.value(); + fence->waitForever(LOG_TAG); } void drawEmptyLayers() { @@ -662,26 +638,13 @@ public: std::unique_ptr<renderengine::RenderEngine> mRE; std::shared_ptr<renderengine::ExternalTexture> mBuffer; - // GLESRenderEngine for testing GLES-specific behavior. - // Owened by mRE, but this is downcasted. - renderengine::gl::GLESRenderEngine* mGLESRE = nullptr; std::vector<uint32_t> mTexNames; }; void RenderEngineTest::initializeRenderEngine() { const auto& renderEngineFactory = GetParam(); - if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - // Only GLESRenderEngine exposes test-only methods. Provide a pointer to the - // GLESRenderEngine if we're using it so that we don't need to dynamic_cast - // every time. - std::unique_ptr<renderengine::gl::GLESRenderEngine> renderEngine = - renderEngineFactory->createGLESRenderEngine(); - mGLESRE = renderEngine.get(); - mRE = std::move(renderEngine); - } else { - mRE = renderEngineFactory->createRenderEngine(); - } + mRE = renderEngineFactory->createRenderEngine(); mBuffer = allocateDefaultBuffer(); } @@ -1002,9 +965,9 @@ void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace( std::vector<renderengine::LayerSettings> layers; renderengine::LayerSettings layer; - layer.sourceDataspace = sourceDataspace; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); + layer.sourceDataspace = sourceDataspace; layer.alpha = 1.0f; // construct a fake color matrix @@ -1030,13 +993,13 @@ void RenderEngineTest::fillBufferColorTransform() { template <typename SourceVariant> void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() { unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap; - dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255}; - dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255}; - dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::V0_BT709] = {77, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::BT2020] = {101, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {75, 0, 0, 255}; ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>( ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 | ui::Dataspace::RANGE_FULL); - dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255}; + dataspaceToColorMap[customizedDataspace] = {61, 0, 0, 255}; for (const auto& [sourceDataspace, color] : dataspaceToColorMap) { fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace); expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1); @@ -1076,13 +1039,13 @@ void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace( template <typename SourceVariant> void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() { unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap; - dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255}; - dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255}; - dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::V0_BT709] = {198, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::BT2020] = {187, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {192, 0, 0, 255}; ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>( ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 | ui::Dataspace::RANGE_FULL); - dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255}; + dataspaceToColorMap[customizedDataspace] = {205, 0, 0, 255}; for (const auto& [outputDataspace, color] : dataspaceToColorMap) { fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace); expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1); @@ -1496,13 +1459,13 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 auto buf = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "input"), + ExternalTexture>(sp<GraphicBuffer>::make(kGreyLevels, 1, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "input"), *mRE, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); @@ -1529,13 +1492,13 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 mBuffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "output"), + ExternalTexture>(sp<GraphicBuffer>::make(kGreyLevels, 1, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), *mRE, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); @@ -1597,18 +1560,57 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 expectBufferColor(Rect(kGreyLevels, 1), generator, 2); } +#ifdef RE_SKIAVK +INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, + testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), + std::make_shared<SkiaGLESCMRenderEngineFactory>(), + std::make_shared<SkiaVkRenderEngineFactory>(), + std::make_shared<SkiaVkCMRenderEngineFactory>())); +#else // RE_SKIAVK INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, - testing::Values(std::make_shared<GLESRenderEngineFactory>(), - std::make_shared<GLESCMRenderEngineFactory>(), - std::make_shared<SkiaGLESRenderEngineFactory>(), + testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), std::make_shared<SkiaGLESCMRenderEngineFactory>())); +#endif // RE_SKIAVK TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); drawEmptyLayers(); } +TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } + initializeRenderEngine(); + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + + // add a red layer + renderengine::LayerSettings layerOne{ + .geometry.boundaries = fullscreenRect().toFloatRect(), + .source.solidColor = half3(1.0f, 0.0f, 0.0f), + .alpha = 1.f, + }; + + std::vector<renderengine::LayerSettings> layersFirst{layerOne}; + invokeDraw(settings, layersFirst); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); + + // re-draw with an empty layer above it, and we get a transparent black one + std::vector<renderengine::LayerSettings> layersSecond; + invokeDraw(settings, layersSecond); + expectBufferColor(fullscreenRect(), 0, 0, 0, 0); +} + TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -1640,111 +1642,111 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { } TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { - initializeRenderEngine(); - - renderengine::DisplaySettings settings; - settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; - 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); - std::future<renderengine::RenderEngineResult> result = - mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd()); - - ASSERT_TRUE(result.valid()); - auto [status, fence] = result.get(); - ASSERT_EQ(BAD_VALUE, status); - ASSERT_FALSE(fence.ok()); -} - -TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { - const auto& renderEngineFactory = GetParam(); - - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - // GLES-specific test - return; + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); } - initializeRenderEngine(); renderengine::DisplaySettings settings; settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; - 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); + ftl::Future<FenceResult> future = + mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd()); - std::future<renderengine::RenderEngineResult> result = - mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd()); - ASSERT_TRUE(result.valid()); - auto [status, fence] = result.get(); - - ASSERT_EQ(NO_ERROR, status); - if (fence.ok()) { - sync_wait(fence.get(), -1); - } - - ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId())); - expectBufferColor(fullscreenRect(), 255, 0, 0, 255); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_FALSE(result.ok()); + ASSERT_EQ(BAD_VALUE, result.error()); } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<ColorSourceVariant>(); } @@ -1752,12 +1754,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -1767,12 +1765,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -1780,81 +1774,129 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } @@ -1862,12 +1904,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -1877,12 +1915,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -1890,81 +1924,129 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_o } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } @@ -1972,12 +2054,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -1987,12 +2065,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { - return; - } - // skip for GLESRenderEngine - if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + GTEST_SKIP(); } initializeRenderEngine(); @@ -2000,46 +2074,73 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_b } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferTextureTransform(); } TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithPremultiplyAlpha(); } TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithoutPremultiplyAlpha(); } TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), @@ -2056,6 +2157,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2077,6 +2181,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2099,6 +2206,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2122,6 +2232,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2146,6 +2259,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); @@ -2173,6 +2289,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { } TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2187,28 +2306,36 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layer.alpha = 1.0; layers.push_back(layer); - std::future<renderengine::RenderEngineResult> resultOne = + ftl::Future<FenceResult> futureOne = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); - ASSERT_TRUE(resultOne.valid()); - auto [statusOne, fenceOne] = resultOne.get(); - ASSERT_EQ(NO_ERROR, statusOne); - - std::future<renderengine::RenderEngineResult> resultTwo = - mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne)); - ASSERT_TRUE(resultTwo.valid()); - auto [statusTwo, fenceTwo] = resultTwo.get(); - ASSERT_EQ(NO_ERROR, statusTwo); - if (fenceTwo.ok()) { - sync_wait(fenceTwo.get(), -1); - } + ASSERT_TRUE(futureOne.valid()); + auto resultOne = futureOne.get(); + ASSERT_TRUE(resultOne.ok()); + auto fenceOne = resultOne.value(); + + ftl::Future<FenceResult> futureTwo = + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(fenceOne->dup())); + ASSERT_TRUE(futureTwo.valid()); + auto resultTwo = futureTwo.get(); + ASSERT_TRUE(resultTwo.ok()); + auto fenceTwo = resultTwo.value(); + fenceTwo->waitForever(LOG_TAG); // Only cleanup the first time. - EXPECT_FALSE(mRE->canSkipPostRenderCleanup()); - mRE->cleanupPostRender(); - EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); + if (mRE->canSkipPostRenderCleanup()) { + // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so + // it never gets added to the cleanup list. In those cases, we can skip. + EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK); + } else { + mRE->cleanupPostRender(); + EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); + } } TEST_P(RenderEngineTest, testRoundedCornersCrop) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2259,6 +2386,9 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2304,6 +2434,9 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2381,6 +2514,9 @@ TEST_P(RenderEngineTest, testRoundedCornersXY) { } TEST_P(RenderEngineTest, testClear) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = fullscreenRect(); @@ -2410,6 +2546,9 @@ TEST_P(RenderEngineTest, testClear) { } TEST_P(RenderEngineTest, testDisableBlendingBuffer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = Rect(0, 0, 1, 1); @@ -2457,11 +2596,59 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { expectBufferColor(rect, 0, 128, 0, 128); } -TEST_P(RenderEngineTest, testDimming) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { +TEST_P(RenderEngineTest, testBorder) { + if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { GTEST_SKIP(); } + if (!GetParam()->useColorManagement()) { + GTEST_SKIP(); + } + + initializeRenderEngine(); + + const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB; + + const auto displayRect = Rect(1080, 2280); + renderengine::DisplaySettings display{ + .physicalDisplay = displayRect, + .clip = displayRect, + .outputDataspace = dataspace, + }; + display.borderInfoList.clear(); + renderengine::BorderRenderInfo info; + info.combinedRegion = Region(Rect(99, 99, 199, 199)); + info.width = 20.0f; + info.color = half4{1.0f, 128.0f / 255.0f, 0.0f, 1.0f}; + display.borderInfoList.emplace_back(info); + + const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255)); + const renderengine::LayerSettings greenLayer{ + .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = greenBuffer, + .usePremultipliedAlpha = true, + }, + }, + .alpha = 1.0f, + .sourceDataspace = dataspace, + .whitePointNits = 200.f, + }; + + std::vector<renderengine::LayerSettings> layers; + layers.emplace_back(greenLayer); + invokeDraw(display, layers); + + expectBufferColor(Rect(99, 99, 101, 101), 255, 128, 0, 255, 1); +} + +TEST_P(RenderEngineTest, testDimming) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB_LINEAR; @@ -2534,7 +2721,7 @@ TEST_P(RenderEngineTest, testDimming) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2612,7 +2799,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2675,7 +2862,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2739,10 +2926,10 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_devi } TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { - initializeRenderEngine(); - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); } + initializeRenderEngine(); const auto displayRect = Rect(2, 1); const renderengine::DisplaySettings display{ @@ -2793,6 +2980,9 @@ TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { } TEST_P(RenderEngineTest, test_isOpaque) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = Rect(0, 0, 1, 1); @@ -2844,11 +3034,7 @@ TEST_P(RenderEngineTest, test_isOpaque) { } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->useColorManagement()) { - GTEST_SKIP(); - } - - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { GTEST_SKIP(); } @@ -2865,11 +3051,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->useColorManagement()) { - GTEST_SKIP(); - } - - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { GTEST_SKIP(); } @@ -2886,10 +3068,9 @@ TEST_P(RenderEngineTest, test_tonemapHLGMatches) { } TEST_P(RenderEngineTest, r8_behaves_as_mask) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); } - initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -2947,10 +3128,9 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { } TEST_P(RenderEngineTest, r8_respects_color_transform) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); } - initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -3013,10 +3193,9 @@ TEST_P(RenderEngineTest, r8_respects_color_transform) { } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); } - initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -3082,10 +3261,9 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { } TEST_P(RenderEngineTest, primeShaderCache) { - if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } - initializeRenderEngine(); auto fut = mRE->primeCache(); diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 96851892b4..fe3a16d4bf 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -17,8 +17,10 @@ #include <cutils/properties.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <hardware/gralloc.h> #include <renderengine/impl/ExternalTexture.h> #include <renderengine/mock/RenderEngine.h> +#include <ui/PixelFormat.h> #include "../threaded/RenderEngineThreaded.h" namespace android { @@ -95,18 +97,6 @@ TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) { ASSERT_EQ(dims, result); } -TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) { - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - status_t result = mThreadedRE->isProtected(); - ASSERT_EQ(false, result); -} - -TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) { - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true)); - size_t result = mThreadedRE->isProtected(); - ASSERT_EQ(true, result); -} - TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) { EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); status_t result = mThreadedRE->supportsProtectedContent(); @@ -119,28 +109,6 @@ TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) { ASSERT_EQ(true, result); } -TEST_F(RenderEngineThreadedTest, useProtectedContext) { - EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); - auto& ipExpect = EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true)); - EXPECT_CALL(*mRenderEngine, isProtected()).After(ipExpect).WillOnce(Return(true)); - - mThreadedRE->useProtectedContext(true); - ASSERT_EQ(true, mThreadedRE->isProtected()); - - // call ANY synchronous function to ensure that useProtectedContext has completed. - mThreadedRE->getContextPriority(); - ASSERT_EQ(true, mThreadedRE->isProtected()); -} - -TEST_F(RenderEngineThreadedTest, useProtectedContext_quickReject) { - EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).Times(0); - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - mThreadedRE->useProtectedContext(false); - // call ANY synchronous function to ensure that useProtectedContext has completed. - mThreadedRE->getContextPriority(); -} - TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) { EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true)); EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0); @@ -176,27 +144,86 @@ TEST_F(RenderEngineThreadedTest, drawLayers) { std::vector<renderengine::LayerSettings> layers; std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), *mRenderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), *mRenderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); base::unique_fd bufferFence; + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)); + EXPECT_CALL(*mRenderEngine, drawLayersInternal) + .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); + + ftl::Future<FenceResult> future = + mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_TRUE(result.ok()); +} + +TEST_F(RenderEngineThreadedTest, drawLayers_protectedLayer) { + renderengine::DisplaySettings settings; + auto layerBuffer = sp<GraphicBuffer>::make(); + layerBuffer->usage |= GRALLOC_USAGE_PROTECTED; + renderengine::LayerSettings layer; + layer.source.buffer.buffer = std::make_shared< + renderengine::impl::ExternalTexture>(std::move(layerBuffer), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage:: + READABLE); + std::vector<renderengine::LayerSettings> layers = {std::move(layer)}; + std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + base::unique_fd bufferFence; + + EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); + EXPECT_CALL(*mRenderEngine, drawLayersInternal) + .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); + + ftl::Future<FenceResult> future = + mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_TRUE(result.ok()); +} + +TEST_F(RenderEngineThreadedTest, drawLayers_protectedOutputBuffer) { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + auto graphicBuffer = sp<GraphicBuffer>::make(); + graphicBuffer->usage |= GRALLOC_USAGE_PROTECTED; + std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(std::move(graphicBuffer), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + base::unique_fd bufferFence; + + EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); EXPECT_CALL(*mRenderEngine, drawLayersInternal) - .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&& - resultPromise, + .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> void { - resultPromise->set_value({NO_ERROR, base::unique_fd()}); - }); + base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); - std::future<renderengine::RenderEngineResult> result = + ftl::Future<FenceResult> future = mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); - ASSERT_TRUE(result.valid()); - auto [status, _] = result.get(); - ASSERT_EQ(NO_ERROR, status); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_TRUE(result.ok()); } } // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 203bb54701..8aa41b3e50 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -90,7 +90,6 @@ void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_S } mRenderEngine = factory(); - mIsProtected = mRenderEngine->isProtected(); pthread_setname_np(pthread_self(), mThreadName); @@ -255,41 +254,11 @@ size_t RenderEngineThreaded::getMaxViewportDims() const { return mRenderEngine->getMaxViewportDims(); } -bool RenderEngineThreaded::isProtected() const { - waitUntilInitialized(); - std::lock_guard lock(mThreadMutex); - return mIsProtected; -} - bool RenderEngineThreaded::supportsProtectedContent() const { waitUntilInitialized(); return mRenderEngine->supportsProtectedContent(); } -void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { - if (isProtected() == useProtectedContext || - (useProtectedContext && !supportsProtectedContent())) { - return; - } - - { - std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::useProtectedContext"); - instance.useProtectedContext(useProtectedContext); - if (instance.isProtected() != useProtectedContext) { - ALOGE("Failed to switch RenderEngine context."); - // reset the cached mIsProtected value to a good state, but this does not - // prevent other callers of this method and isProtected from reading the - // invalid cached value. - mIsProtected = instance.isProtected(); - } - }); - mIsProtected = useProtectedContext; - } - mCondition.notify_one(); -} - void RenderEngineThreaded::cleanupPostRender() { if (canSkipPostRenderCleanup()) { return; @@ -313,27 +282,28 @@ bool RenderEngineThreaded::canSkipPostRenderCleanup() const { } void RenderEngineThreaded::drawLayersInternal( - const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence) { - resultPromise->set_value({NO_ERROR, base::unique_fd()}); + resultPromise->set_value(Fence::NO_FENCE); return; } -std::future<RenderEngineResult> RenderEngineThreaded::drawLayers( +ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence) { ATRACE_CALL(); - const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>(); - std::future<RenderEngineResult> resultFuture = resultPromise->get_future(); + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); int fd = bufferFence.release(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache, fd](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::drawLayers"); + instance.updateProtectedContext(layers, buffer); instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, base::unique_fd(fd)); }); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 1340902126..168e2d2b06 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -51,16 +51,14 @@ public: size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; - bool isProtected() const override; bool supportsProtectedContent() const override; - void useProtectedContext(bool useProtectedContext) override; void cleanupPostRender() override; - std::future<RenderEngineResult> drawLayers(const DisplaySettings& display, - const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, - base::unique_fd&& bufferFence) override; + ftl::Future<FenceResult> drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence) override; void cleanFramebufferCache() override; int getContextPriority() override; @@ -73,7 +71,7 @@ protected: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; bool canSkipPostRenderCleanup() const override; - void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, @@ -84,6 +82,9 @@ private: void waitUntilInitialized() const; static status_t setSchedFifo(bool enabled); + // No-op. This method is only called on leaf implementations of RenderEngine. + void useProtectedContext(bool) override {} + /* ------------------------------------------------------------------------ * Threading */ @@ -107,7 +108,6 @@ private: * Render Engine */ std::unique_ptr<renderengine::RenderEngine> mRenderEngine; - std::atomic<bool> mIsProtected = false; }; } // namespace threaded } // namespace renderengine diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index a6cacad374..2278d391b5 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -22,12 +22,12 @@ #include <cutils/native_handle.h> #include <utils/Errors.h> #include <utils/RefBase.h> -#include <utils/Vector.h> #include <utils/Timers.h> +#include <utils/Vector.h> -#include <binder/Parcel.h> #include <binder/IInterface.h> #include <binder/IResultReceiver.h> +#include <binder/Parcel.h> #include <sensor/Sensor.h> #include <sensor/ISensorEventConnection.h> @@ -42,6 +42,7 @@ enum { GET_DYNAMIC_SENSOR_LIST, CREATE_SENSOR_DIRECT_CONNECTION, SET_OPERATION_PARAMETER, + GET_RUNTIME_SENSOR_LIST, }; class BpSensorServer : public BpInterface<ISensorServer> @@ -90,6 +91,25 @@ public: return v; } + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) + { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + data.writeString16(opPackageName); + data.writeInt32(deviceId); + remote()->transact(GET_RUNTIME_SENSOR_LIST, data, &reply); + Sensor s; + Vector<Sensor> v; + uint32_t n = reply.readUint32(); + v.setCapacity(n); + while (n) { + n--; + reply.read(s); + v.add(s); + } + return v; + } + virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName, const String16& attributionTag) { @@ -194,6 +214,18 @@ status_t BnSensorServer::onTransact( } return NO_ERROR; } + case GET_RUNTIME_SENSOR_LIST: { + CHECK_INTERFACE(ISensorServer, data, reply); + const String16& opPackageName = data.readString16(); + const int deviceId = data.readInt32(); + Vector<Sensor> v(getRuntimeSensorList(opPackageName, deviceId)); + size_t n = v.size(); + reply->writeUint32(static_cast<uint32_t>(n)); + for (size_t i = 0; i < n; i++) { + reply->write(v[i]); + } + return NO_ERROR; + } case CREATE_SENSOR_DIRECT_CONNECTION: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); @@ -205,9 +237,10 @@ status_t BnSensorServer::onTransact( if (resource == nullptr) { return BAD_VALUE; } + native_handle_set_fdsan_tag(resource); sp<ISensorEventConnection> ch = createSensorDirectConnection(opPackageName, size, type, format, resource); - native_handle_close(resource); + native_handle_close_with_tag(resource); native_handle_delete(resource); reply->writeStrongBinder(IInterface::asBinder(ch)); return NO_ERROR; diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 0ba9704263..27482768f2 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -201,6 +201,19 @@ ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) { return static_cast<ssize_t>(count); } +ssize_t SensorManager::getRuntimeSensorList(int deviceId, Vector<Sensor>& runtimeSensors) { + Mutex::Autolock _l(mLock); + status_t err = assertStateLocked(); + if (err < 0) { + return static_cast<ssize_t>(err); + } + + runtimeSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, deviceId); + size_t count = runtimeSensors.size(); + + return static_cast<ssize_t>(count); +} + ssize_t SensorManager::getDynamicSensorList(Sensor const* const** list) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h index ce5c672da0..3295196ac4 100644 --- a/libs/sensor/include/sensor/ISensorServer.h +++ b/libs/sensor/include/sensor/ISensorServer.h @@ -43,6 +43,7 @@ public: virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0; virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0; + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) = 0; virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName, const String16& attributionTag) = 0; diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index 8d0a8a45d9..0798da292a 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -59,6 +59,7 @@ public: ssize_t getSensorList(Sensor const* const** list); ssize_t getDynamicSensorList(Vector<Sensor>& list); ssize_t getDynamicSensorList(Sensor const* const** list); + ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list); Sensor const* getDefaultSensor(int type); sp<SensorEventQueue> createEventQueue( String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16("")); diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 847747948a..960f845488 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -23,13 +23,14 @@ package { cc_library_static { name: "libshaders", - + defaults: [ + "android.hardware.graphics.common-ndk_shared", + "android.hardware.graphics.composer3-ndk_shared", + ], export_include_dirs: ["include"], local_include_dirs: ["include"], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.graphics.common@1.2", "libnativewindow", ], diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h index 2a4a370078..42b0cc131c 100644 --- a/libs/shaders/include/shaders/shaders.h +++ b/libs/shaders/include/shaders/shaders.h @@ -68,6 +68,9 @@ struct LinearEffect { // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output // dataspace for correct conversion to linear colors. ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN; + + enum SkSLType { Shader, ColorFilter }; + SkSLType type = Shader; }; static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) { @@ -96,6 +99,10 @@ struct LinearEffectHasher { // 2. Apply color transform matrices in linear space std::string buildLinearEffectSkSL(const LinearEffect& linearEffect); +// Generates a shader string that applies color transforms in linear space. +// This is intended to be plugged into an SkColorFilter +std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect); + // Generates a list of uniforms to set on the LinearEffect shader above. std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms( const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance, diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp index f80e93f6f8..a3c403e551 100644 --- a/libs/shaders/shaders.cpp +++ b/libs/shaders/shaders.cpp @@ -386,12 +386,23 @@ void generateOETF(ui::Dataspace dataspace, std::string& shader) { } } -void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { - shader.append(R"( - uniform shader child; - half4 main(float2 xy) { - float4 c = float4(child.eval(xy)); - )"); +void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type, + std::string& shader) { + switch (type) { + case LinearEffect::SkSLType::ColorFilter: + shader.append(R"( + half4 main(half4 inputColor) { + float4 c = float4(inputColor); + )"); + break; + case LinearEffect::SkSLType::Shader: + shader.append(R"( + uniform shader child; + half4 main(float2 xy) { + float4 c = float4(child.eval(xy)); + )"); + break; + } if (undoPremultipliedAlpha) { shader.append(R"( c.rgb = c.rgb / (c.a + 0.0019); @@ -459,7 +470,7 @@ std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { generateXYZTransforms(shaderString); generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString); generateOETF(linearEffect.outputDataspace, shaderString); - generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString); + generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString); return shaderString; } diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp index 718d37bc38..1e4f45ac45 100644 --- a/libs/shaders/tests/Android.bp +++ b/libs/shaders/tests/Android.bp @@ -23,6 +23,10 @@ package { cc_test { name: "libshaders_test", + defaults: [ + "android.hardware.graphics.common-ndk_shared", + "android.hardware.graphics.composer3-ndk_shared", + ], test_suites: ["device-tests"], srcs: [ "shaders_test.cpp", @@ -31,8 +35,6 @@ cc_test { "libtonemap_headers", ], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.graphics.common@1.2", "libnativewindow", ], diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index eca051d215..8c8815d0e4 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -23,13 +23,15 @@ package { cc_library_static { name: "libtonemap", + defaults: [ + "android.hardware.graphics.common-ndk_shared", + "android.hardware.graphics.composer3-ndk_shared", + ], vendor_available: true, local_include_dirs: ["include"], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", - "android.hardware.graphics.composer3-V1-ndk", "liblog", "libnativewindow", ], diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index 0002d3a935..2abf51563c 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -23,6 +23,10 @@ package { cc_test { name: "libtonemap_test", + defaults: [ + "android.hardware.graphics.common-ndk_shared", + "android.hardware.graphics.composer3-ndk_shared", + ], test_suites: ["device-tests"], srcs: [ "tonemap_test.cpp", @@ -31,8 +35,6 @@ cc_test { "libtonemap_headers", ], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", - "android.hardware.graphics.composer3-V1-ndk", "libnativewindow", ], static_libs: [ diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 98d9b94322..d33dd34d4e 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -127,7 +127,6 @@ cc_library_shared { "DebugUtils.cpp", "DeviceProductInfo.cpp", "DisplayIdentification.cpp", - "DisplayMode.cpp", "DynamicDisplayInfo.cpp", "Fence.cpp", "FenceTime.cpp", @@ -139,11 +138,9 @@ cc_library_shared { "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", - "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", "StaticAsserts.cpp", - "StaticDisplayInfo.cpp", ], include_dirs: [ @@ -240,10 +237,6 @@ cc_library_shared { ], afdo: true, - - header_abi_checker: { - diff_flags: ["-allow-adding-removing-weak-symbols"], - }, } cc_library_headers { diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp index 04d9d3c989..6ae27dee1d 100644 --- a/libs/ui/DeviceProductInfo.cpp +++ b/libs/ui/DeviceProductInfo.cpp @@ -14,74 +14,43 @@ * limitations under the License. */ +#include <ftl/match.h> #include <ui/DeviceProductInfo.h> #include <android-base/stringprintf.h> -#include <ui/FlattenableHelpers.h> -#include <utils/Log.h> - -#define RETURN_IF_ERROR(op) \ - if (const status_t status = (op); status != OK) return status; namespace android { -using base::StringAppendF; - -size_t DeviceProductInfo::getFlattenedSize() const { - return FlattenableHelpers::getFlattenedSize(name) + - FlattenableHelpers::getFlattenedSize(manufacturerPnpId) + - FlattenableHelpers::getFlattenedSize(productId) + - FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) + - FlattenableHelpers::getFlattenedSize(relativeAddress); -} - -status_t DeviceProductInfo::flatten(void* buffer, size_t size) const { - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress)); - return OK; -} - -status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) { - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress)); - return OK; -} - -void DeviceProductInfo::dump(std::string& result) const { - StringAppendF(&result, "{name=\"%s\", ", name.c_str()); - StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data()); - StringAppendF(&result, "productId=%s, ", productId.c_str()); - - if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) { - StringAppendF(&result, "modelYear=%u, ", model->year); - } else if (const auto* manufactureWeekAndYear = - std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) { - StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week); - StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year); - } else if (const auto* manufactureYear = - std::get_if<ManufactureYear>(&manufactureOrModelDate)) { - StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year); - } else { - ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate"); - } +std::string to_string(const DeviceProductInfo& info) { + using base::StringAppendF; + + std::string result; + StringAppendF(&result, "{name=\"%s\", ", info.name.c_str()); + StringAppendF(&result, "manufacturerPnpId=%s, ", info.manufacturerPnpId.data()); + StringAppendF(&result, "productId=%s, ", info.productId.c_str()); + + ftl::match( + info.manufactureOrModelDate, + [&](DeviceProductInfo::ModelYear model) { + StringAppendF(&result, "modelYear=%u, ", model.year); + }, + [&](DeviceProductInfo::ManufactureWeekAndYear manufacture) { + StringAppendF(&result, "manufactureWeek=%u, ", manufacture.week); + StringAppendF(&result, "manufactureYear=%d, ", manufacture.year); + }, + [&](DeviceProductInfo::ManufactureYear manufacture) { + StringAppendF(&result, "manufactureYear=%d, ", manufacture.year); + }); result.append("relativeAddress=["); - for (size_t i = 0; i < relativeAddress.size(); i++) { + for (size_t i = 0; i < info.relativeAddress.size(); i++) { if (i != 0) { result.append(", "); } - StringAppendF(&result, "%u", relativeAddress[i]); + StringAppendF(&result, "%u", info.relativeAddress[i]); } result.append("]}"); + return result; } } // namespace android diff --git a/libs/ui/DisplayMode.cpp b/libs/ui/DisplayMode.cpp deleted file mode 100644 index cf05dbfb05..0000000000 --- a/libs/ui/DisplayMode.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2021 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/DisplayMode.h> - -#include <cstdint> - -#include <ui/FlattenableHelpers.h> - -#define RETURN_IF_ERROR(op) \ - if (const status_t status = (op); status != OK) return status; - -namespace android::ui { - -size_t DisplayMode::getFlattenedSize() const { - return FlattenableHelpers::getFlattenedSize(id) + - FlattenableHelpers::getFlattenedSize(resolution) + - FlattenableHelpers::getFlattenedSize(xDpi) + - FlattenableHelpers::getFlattenedSize(yDpi) + - FlattenableHelpers::getFlattenedSize(refreshRate) + - FlattenableHelpers::getFlattenedSize(appVsyncOffset) + - FlattenableHelpers::getFlattenedSize(sfVsyncOffset) + - FlattenableHelpers::getFlattenedSize(presentationDeadline) + - FlattenableHelpers::getFlattenedSize(group); -} - -status_t DisplayMode::flatten(void* buffer, size_t size) const { - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, id)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, resolution)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, xDpi)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, yDpi)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, refreshRate)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, appVsyncOffset)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, sfVsyncOffset)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, presentationDeadline)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, group)); - return OK; -} - -status_t DisplayMode::unflatten(const void* buffer, size_t size) { - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &id)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &resolution)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &xDpi)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &yDpi)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &refreshRate)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &appVsyncOffset)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &sfVsyncOffset)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &presentationDeadline)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &group)); - return OK; -} - -} // namespace android::ui diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp index 78ba9965b6..f5feea925d 100644 --- a/libs/ui/DynamicDisplayInfo.cpp +++ b/libs/ui/DynamicDisplayInfo.cpp @@ -18,11 +18,6 @@ #include <cstdint> -#include <ui/FlattenableHelpers.h> - -#define RETURN_IF_ERROR(op) \ - if (const status_t status = (op); status != OK) return status; - namespace android::ui { std::optional<ui::DisplayMode> DynamicDisplayInfo::getActiveDisplayMode() const { @@ -34,42 +29,4 @@ std::optional<ui::DisplayMode> DynamicDisplayInfo::getActiveDisplayMode() const return {}; } -size_t DynamicDisplayInfo::getFlattenedSize() const { - return FlattenableHelpers::getFlattenedSize(supportedDisplayModes) + - FlattenableHelpers::getFlattenedSize(activeDisplayModeId) + - FlattenableHelpers::getFlattenedSize(supportedColorModes) + - FlattenableHelpers::getFlattenedSize(activeColorMode) + - FlattenableHelpers::getFlattenedSize(hdrCapabilities) + - FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) + - FlattenableHelpers::getFlattenedSize(gameContentTypeSupported) + - FlattenableHelpers::getFlattenedSize(preferredBootDisplayMode); -} - -status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const { - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedDisplayModes)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeDisplayModeId)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedColorModes)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeColorMode)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, preferredBootDisplayMode)); - return OK; -} - -status_t DynamicDisplayInfo::unflatten(const void* buffer, size_t size) { - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedDisplayModes)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeDisplayModeId)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedColorModes)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeColorMode)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &preferredBootDisplayMode)); - return OK; -} - } // namespace android::ui diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp deleted file mode 100644 index aec2fac780..0000000000 --- a/libs/ui/HdrCapabilities.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2016 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/HdrCapabilities.h> - -namespace android { - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" -#endif - -size_t HdrCapabilities::getFlattenedSize() const { - return sizeof(mMaxLuminance) + - sizeof(mMaxAverageLuminance) + - sizeof(mMinLuminance) + - sizeof(int32_t) + - mSupportedHdrTypes.size() * sizeof(ui::Hdr); -} - -status_t HdrCapabilities::flatten(void* buffer, size_t size) const { - - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - - int32_t* const buf = static_cast<int32_t*>(buffer); - reinterpret_cast<float&>(buf[0]) = mMaxLuminance; - reinterpret_cast<float&>(buf[1]) = mMaxAverageLuminance; - reinterpret_cast<float&>(buf[2]) = mMinLuminance; - buf[3] = static_cast<int32_t>(mSupportedHdrTypes.size()); - for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) { - buf[4 + i] = static_cast<int32_t>(mSupportedHdrTypes[i]); - } - return NO_ERROR; -} - -status_t HdrCapabilities::unflatten(void const* buffer, size_t size) { - - size_t minSize = sizeof(mMaxLuminance) + - sizeof(mMaxAverageLuminance) + - sizeof(mMinLuminance) + - sizeof(int32_t); - - if (size < minSize) { - return NO_MEMORY; - } - - int32_t const * const buf = static_cast<int32_t const *>(buffer); - const size_t itemCount = size_t(buf[3]); - - // check the buffer is large enough - if (size < minSize + itemCount * sizeof(int32_t)) { - return BAD_VALUE; - } - - mMaxLuminance = reinterpret_cast<float const&>(buf[0]); - mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]); - mMinLuminance = reinterpret_cast<float const&>(buf[2]); - if (itemCount) { - mSupportedHdrTypes.resize(itemCount); - for (size_t i = 0; i < itemCount; ++i) { - mSupportedHdrTypes[i] = static_cast<ui::Hdr>(buf[4 + i]); - } - } - return NO_ERROR; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -} // namespace android diff --git a/libs/ui/StaticDisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp deleted file mode 100644 index 03d15e4694..0000000000 --- a/libs/ui/StaticDisplayInfo.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2020 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/StaticDisplayInfo.h> - -#include <cstdint> - -#include <ui/FlattenableHelpers.h> - -#define RETURN_IF_ERROR(op) \ - if (const status_t status = (op); status != OK) return status; - -namespace android::ui { - -size_t StaticDisplayInfo::getFlattenedSize() const { - return FlattenableHelpers::getFlattenedSize(connectionType) + - FlattenableHelpers::getFlattenedSize(density) + - FlattenableHelpers::getFlattenedSize(secure) + - FlattenableHelpers::getFlattenedSize(deviceProductInfo) + - FlattenableHelpers::getFlattenedSize(installOrientation); -} - -status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const { - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo)); - RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, installOrientation)); - return OK; -} - -status_t StaticDisplayInfo::unflatten(void const* buffer, size_t size) { - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo)); - RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &installOrientation)); - return OK; -} - -} // namespace android::ui diff --git a/libs/ui/include/ui/ColorMode.h b/libs/ui/include/ui/ColorMode.h new file mode 100644 index 0000000000..a47eaed171 --- /dev/null +++ b/libs/ui/include/ui/ColorMode.h @@ -0,0 +1,60 @@ +/* + * Copyright 2022 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 <vector> + +#include <ui/GraphicTypes.h> + +namespace android::ui { + +using ColorModes = std::vector<ColorMode>; + +inline bool isWideColorMode(ColorMode colorMode) { + switch (colorMode) { + case ColorMode::DISPLAY_P3: + case ColorMode::ADOBE_RGB: + case ColorMode::DCI_P3: + case ColorMode::BT2020: + case ColorMode::DISPLAY_BT2020: + case ColorMode::BT2100_PQ: + case ColorMode::BT2100_HLG: + return true; + 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; + } +} + +inline Dataspace pickDataspaceFor(ColorMode colorMode) { + switch (colorMode) { + case ColorMode::DISPLAY_P3: + case ColorMode::BT2100_PQ: + case ColorMode::BT2100_HLG: + case ColorMode::DISPLAY_BT2020: + return Dataspace::DISPLAY_P3; + default: + return Dataspace::V0_SRGB; + } +} + +} // namespace android::ui diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h index 807a5d96a3..4229cf1507 100644 --- a/libs/ui/include/ui/DeviceProductInfo.h +++ b/libs/ui/include/ui/DeviceProductInfo.h @@ -24,8 +24,6 @@ #include <variant> #include <vector> -#include <utils/Flattenable.h> - namespace android { // NUL-terminated plug and play ID. @@ -34,7 +32,7 @@ using PnpId = std::array<char, 4>; // Product-specific information about the display or the directly connected device on the // display chain. For example, if the display is transitively connected, this field may contain // product information about the intermediate device. -struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> { +struct DeviceProductInfo { struct ModelYear { uint32_t year; }; @@ -63,13 +61,8 @@ struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> { // address is unavailable. // For example, for HDMI connected device this will be the physical address. std::vector<uint8_t> relativeAddress; - - bool isFixedSize() const { return false; } - size_t getFlattenedSize() const; - status_t flatten(void* buffer, size_t size) const; - status_t unflatten(void const* buffer, size_t size); - - void dump(std::string& result) const; }; +std::string to_string(const DeviceProductInfo&); + } // namespace android diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index 9120972a42..3a31fa0848 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -17,9 +17,11 @@ #pragma once #include <cstdint> -#include <optional> +#include <ostream> #include <string> +#include <ftl/optional.h> + namespace android { // ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t. @@ -66,9 +68,14 @@ inline std::string to_string(DisplayId displayId) { return std::to_string(displayId.value); } +// For tests. +inline std::ostream& operator<<(std::ostream& stream, DisplayId displayId) { + return stream << "DisplayId{" << displayId.value << '}'; +} + // DisplayId of a physical display, such as the internal display or externally connected display. struct PhysicalDisplayId : DisplayId { - static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) { + static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) { if (id.value & FLAG_VIRTUAL) { return std::nullopt; } diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h index 56f68e7bb2..65a8769c98 100644 --- a/libs/ui/include/ui/DisplayMode.h +++ b/libs/ui/include/ui/DisplayMode.h @@ -19,6 +19,7 @@ #include <cstdint> #include <type_traits> +#include <ui/GraphicTypes.h> #include <ui/Size.h> #include <utils/Flattenable.h> #include <utils/Timers.h> @@ -29,22 +30,18 @@ namespace android::ui { using DisplayModeId = int32_t; // Mode supported by physical display. -struct DisplayMode : LightFlattenable<DisplayMode> { +struct DisplayMode { DisplayModeId id; ui::Size resolution; float xDpi = 0; float yDpi = 0; + std::vector<ui::Hdr> supportedHdrTypes; float refreshRate = 0; nsecs_t appVsyncOffset = 0; nsecs_t sfVsyncOffset = 0; nsecs_t presentationDeadline = 0; int32_t group = -1; - - bool isFixedSize() const { return false; } - size_t getFlattenedSize() const; - status_t flatten(void* buffer, size_t size) const; - status_t unflatten(const void* buffer, size_t size); }; } // namespace android::ui diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h index ce75a65214..0b77754455 100644 --- a/libs/ui/include/ui/DynamicDisplayInfo.h +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -24,18 +24,18 @@ #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> -#include <utils/Flattenable.h> namespace android::ui { // Information about a physical display which may change on hotplug reconnect. -struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> { +struct DynamicDisplayInfo { std::vector<ui::DisplayMode> supportedDisplayModes; // This struct is going to be serialized over binder, so // we can't use size_t because it may have different width // in the client process. ui::DisplayModeId activeDisplayModeId; + float renderFrameRate; std::vector<ui::ColorMode> supportedColorModes; ui::ColorMode activeColorMode; @@ -53,11 +53,6 @@ struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> { ui::DisplayModeId preferredBootDisplayMode; std::optional<ui::DisplayMode> getActiveDisplayMode() const; - - bool isFixedSize() const { return false; } - size_t getFlattenedSize() const; - status_t flatten(void* buffer, size_t size) const; - status_t unflatten(const void* buffer, size_t size); }; } // namespace android::ui diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h b/libs/ui/include/ui/FenceResult.h index 0ce263b930..6d63fc9973 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h +++ b/libs/ui/include/ui/FenceResult.h @@ -20,30 +20,14 @@ #include <utils/Errors.h> #include <utils/StrongPointer.h> -// TODO(b/232535621): Pull this file to <ui/FenceResult.h> so that RenderEngine::drawLayers returns -// FenceResult rather than RenderEngineResult. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <renderengine/RenderEngine.h> -#pragma clang diagnostic pop - namespace android { class Fence; using FenceResult = base::expected<sp<Fence>, status_t>; -// TODO(b/232535621): Prevent base::unexpected(NO_ERROR) from being a valid FenceResult. inline status_t fenceStatus(const FenceResult& fenceResult) { return fenceResult.ok() ? NO_ERROR : fenceResult.error(); } -inline FenceResult toFenceResult(renderengine::RenderEngineResult&& result) { - if (auto [status, fence] = std::move(result); fence.ok()) { - return sp<Fence>::make(std::move(fence)); - } else { - return base::unexpected(status); - } -} - } // namespace android diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 57be686592..dbe475b805 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -270,7 +270,7 @@ private: // Send a callback when a GraphicBuffer dies. // - // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When + // This is used for layer caching. GraphicBuffers are refcounted per process. When // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes // problems when trying to implicitcly cache across process boundaries. Ideally, both sides // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h index 8661c36676..1775d390f0 100644 --- a/libs/ui/include/ui/GraphicTypes.h +++ b/libs/ui/include/ui/GraphicTypes.h @@ -20,6 +20,7 @@ #include <aidl/android/hardware/graphics/common/ChromaSiting.h> #include <aidl/android/hardware/graphics/common/Compression.h> #include <aidl/android/hardware/graphics/common/Cta861_3.h> +#include <aidl/android/hardware/graphics/common/Hdr.h> #include <aidl/android/hardware/graphics/common/Interlaced.h> #include <aidl/android/hardware/graphics/common/PlaneLayout.h> #include <aidl/android/hardware/graphics/common/Smpte2086.h> @@ -42,7 +43,6 @@ namespace ui { using android::hardware::graphics::common::V1_1::RenderIntent; using android::hardware::graphics::common::V1_2::ColorMode; using android::hardware::graphics::common::V1_2::Dataspace; -using android::hardware::graphics::common::V1_2::Hdr; using android::hardware::graphics::common::V1_2::PixelFormat; /** @@ -50,6 +50,7 @@ using android::hardware::graphics::common::V1_2::PixelFormat; */ using aidl::android::hardware::graphics::common::BlendMode; using aidl::android::hardware::graphics::common::Cta861_3; +using aidl::android::hardware::graphics::common::Hdr; using aidl::android::hardware::graphics::common::PlaneLayout; using aidl::android::hardware::graphics::common::Smpte2086; diff --git a/libs/ui/include/ui/HdrCapabilities.h b/libs/ui/include/ui/HdrCapabilities.h index 813addeca6..ae54223585 100644 --- a/libs/ui/include/ui/HdrCapabilities.h +++ b/libs/ui/include/ui/HdrCapabilities.h @@ -22,12 +22,10 @@ #include <vector> #include <ui/GraphicTypes.h> -#include <utils/Flattenable.h> namespace android { -class HdrCapabilities : public LightFlattenable<HdrCapabilities> -{ +class HdrCapabilities { public: HdrCapabilities(const std::vector<ui::Hdr>& types, float maxLuminance, float maxAverageLuminance, float minLuminance) @@ -49,12 +47,6 @@ public: float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; } float getDesiredMinLuminance() const { return mMinLuminance; } - // Flattenable protocol - bool isFixedSize() const { return false; } - size_t getFlattenedSize() const; - status_t flatten(void* buffer, size_t size) const; - status_t unflatten(void const* buffer, size_t size); - private: std::vector<ui::Hdr> mSupportedHdrTypes; float mMaxLuminance; diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h index 83d431dea3..c1d60f4f6c 100644 --- a/libs/ui/include/ui/Rotation.h +++ b/libs/ui/include/ui/Rotation.h @@ -20,7 +20,14 @@ namespace android::ui { -enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 }; +enum class Rotation { + Rotation0 = 0, + Rotation90 = 1, + Rotation180 = 2, + Rotation270 = 3, + + ftl_last = Rotation270 +}; // Equivalent to Surface.java constants. constexpr auto ROTATION_0 = Rotation::Rotation0; diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h index cc7c869b3b..83da821f37 100644 --- a/libs/ui/include/ui/StaticDisplayInfo.h +++ b/libs/ui/include/ui/StaticDisplayInfo.h @@ -20,24 +20,18 @@ #include <ui/DeviceProductInfo.h> #include <ui/Rotation.h> -#include <utils/Flattenable.h> namespace android::ui { -enum class DisplayConnectionType { Internal, External }; +enum class DisplayConnectionType { Internal, External, ftl_last = External }; // Immutable information about physical display. -struct StaticDisplayInfo : LightFlattenable<StaticDisplayInfo> { +struct StaticDisplayInfo { DisplayConnectionType connectionType = DisplayConnectionType::Internal; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; Rotation installOrientation = ROTATION_0; - - bool isFixedSize() const { return false; } - size_t getFlattenedSize() const; - status_t flatten(void* buffer, size_t size) const; - status_t unflatten(void const* buffer, size_t size); }; } // namespace android::ui diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index ec906458a3..80e911c65d 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -22,15 +22,6 @@ #include <log/log.h> #include <utils/Errors.h> - -// To guarantee if HapticScale enum has the same value as IExternalVibratorService -static_assert(static_cast<int>(android::os::HapticScale::MUTE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_MUTE)); -static_assert(static_cast<int>(android::os::HapticScale::VERY_LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_LOW)); -static_assert(static_cast<int>(android::os::HapticScale::LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_LOW)); -static_assert(static_cast<int>(android::os::HapticScale::NONE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_NONE)); -static_assert(static_cast<int>(android::os::HapticScale::HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_HIGH)); -static_assert(static_cast<int>(android::os::HapticScale::VERY_HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_HIGH)); - void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) { out->writeInt32(attrs.usage); out->writeInt32(attrs.content_type); @@ -74,5 +65,25 @@ inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const { return mToken == rhs.mToken; } +os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) { + switch (externalVibrationScale) { + case IExternalVibratorService::SCALE_MUTE: + return os::HapticScale::MUTE; + case IExternalVibratorService::SCALE_VERY_LOW: + return os::HapticScale::VERY_LOW; + case IExternalVibratorService::SCALE_LOW: + return os::HapticScale::LOW; + case IExternalVibratorService::SCALE_NONE: + return os::HapticScale::NONE; + case IExternalVibratorService::SCALE_HIGH: + return os::HapticScale::HIGH; + case IExternalVibratorService::SCALE_VERY_HIGH: + return os::HapticScale::VERY_HIGH; + default: + ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale); + return os::HapticScale::NONE; + } +} + } // namespace os } // namespace android diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index 760dbce149..00cd3cd256 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -23,6 +23,7 @@ #include <binder/Parcelable.h> #include <system/audio.h> #include <utils/RefBase.h> +#include <vibrator/ExternalVibrationUtils.h> namespace android { namespace os { @@ -44,6 +45,10 @@ public : audio_attributes_t getAudioAttributes() const { return mAttrs; } sp<IExternalVibrationController> getController() { return mController; } + /* Converts the scale from non-public ExternalVibrationService into the HapticScale + * used by the utils. + */ + static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale); private: int32_t mUid; @@ -53,7 +58,7 @@ private: sp<IBinder> mToken = new BBinder(); }; -} // namespace android } // namespace os +} // namespace android #endif // ANDROID_EXTERNAL_VIBRATION_H diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index c588bfdedd..ca219d3cbf 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -19,8 +19,6 @@ namespace android::os { -// Copied from frameworks/base/core/java/android/os/IExternalVibratorService.aidl -// The values are checked in ExternalVibration.cpp enum class HapticScale { MUTE = -100, VERY_LOW = -2, diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp index d38f2eff01..f1122fd098 100644 --- a/opengl/libs/EGL/egl_angle_platform.cpp +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -68,9 +68,9 @@ static void logInfo(PlatformMethods* /*platform*/, const char* infoMessage) { static TraceEventHandle addTraceEvent( PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/, - const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/, - const char** /*arg_names*/, const unsigned char* /*arg_types*/, - const unsigned long long* /*arg_values*/, unsigned char /*flags*/) { + const char* name, unsigned long long /*id*/, double /*timestamp*/, int num_args, + const char** arg_names, const unsigned char* /*arg_types*/, + const unsigned long long* arg_values, unsigned char /*flags*/) { switch (phase) { case 'B': { ATRACE_BEGIN(name); @@ -84,6 +84,13 @@ static TraceEventHandle addTraceEvent( ATRACE_NAME(name); break; } + case 'C': { + for(int i=0; i<num_args ; i++) + { + ATRACE_INT(arg_names[i],arg_values[i]); + } + break; + } default: // Could handle other event types here break; diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index efa67dbc1c..8348d6cb0a 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -28,7 +28,7 @@ // Cache size limits. static const size_t maxKeySize = 12 * 1024; static const size_t maxValueSize = 64 * 1024; -static const size_t maxTotalSize = 2 * 1024 * 1024; +static const size_t maxTotalSize = 32 * 1024 * 1024; // The time in seconds to wait before saving newly inserted cache entries. static const unsigned int deferredSaveDelay = 4; diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp index fd4522e757..e94b565f11 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -36,7 +36,14 @@ WindowSurface::WindowSurface() { return; } - const auto displayToken = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + if (ids.empty()) { + fprintf(stderr, "Failed to get ID for any displays.\n"); + return; + } + + // display 0 is picked for now, can extend to support all displays if needed + const auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); if (displayToken == nullptr) { fprintf(stderr, "ERROR: no display\n"); return; diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp index ae1bb1a0d0..3ef5049230 100644 --- a/services/audiomanager/IAudioManager.cpp +++ b/services/audiomanager/IAudioManager.cpp @@ -87,12 +87,12 @@ public: } virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, - audio_port_handle_t deviceId) { + audio_port_handle_t eventId) { Parcel data, reply; data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); data.writeInt32((int32_t) piid); data.writeInt32((int32_t) event); - data.writeInt32((int32_t) deviceId); + data.writeInt32((int32_t) eventId); return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY); } @@ -141,6 +141,17 @@ public: data.writeInt32((int32_t) sessionId); return remote()->transact(PLAYER_SESSION_ID, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual status_t portEvent(audio_port_handle_t portId, player_state_t event, + const std::unique_ptr<os::PersistableBundle>& extras) { + Parcel data, reply; + data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); + data.writeInt32((int32_t) portId); + data.writeInt32((int32_t) event); + // TODO: replace PersistableBundle with own struct + data.writeNullableParcelable(extras); + return remote()->transact(PORT_EVENT, data, &reply, IBinder::FLAG_ONEWAY); + } }; IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService"); diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp index 1e3799185e..9b783916d3 100644 --- a/services/batteryservice/Android.bp +++ b/services/batteryservice/Android.bp @@ -9,6 +9,7 @@ package { cc_library_headers { name: "libbatteryservice_headers", + host_supported: true, vendor_available: true, recovery_available: true, export_include_dirs: ["include"], diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 18d670ae38..b8854352ad 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -38,9 +38,12 @@ cc_defaults { "-Wshadow", "-Wshadow-field-in-constructor-modified", "-Wshadow-uncaptured-local", + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], sanitize: { - misc_undefined: ["bounds"], + misc_undefined: [ + "bounds", + ], }, tidy: true, tidy_checks: [ @@ -56,11 +59,10 @@ cc_defaults { filegroup { name: "libinputflinger_sources", srcs: [ - "InputClassifier.cpp", "InputCommonConverter.cpp", + "InputProcessor.cpp", "PreferStylusOverTouchBlocker.cpp", "UnwantedInteractionBlocker.cpp", - "InputManager.cpp", ], } @@ -76,21 +78,35 @@ cc_defaults { "libcrypto", "libcutils", "libhidlbase", - "libinput", "libkll", "liblog", "libprotobuf-cpp-lite", "libstatslog", - "libstatspull", - "libstatssocket", "libutils", - "libui", "server_configurable_flags", ], static_libs: [ "libattestation", "libpalmrejection", + "libui-types", ], + target: { + android: { + shared_libs: [ + "libgui", + "libinput", + "libstatspull", + "libstatssocket", + ], + }, + host: { + static_libs: [ + "libinput", + "libstatspull", + "libstatssocket", + ], + }, + }, } cc_library_shared { @@ -99,6 +115,10 @@ cc_library_shared { "inputflinger_defaults", "libinputflinger_defaults", ], + srcs: [ + "InputManager.cpp", + // other sources are added via "defaults" + ], cflags: [ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden @@ -107,9 +127,8 @@ cc_library_shared { // This should consist only of dependencies from inputflinger. Other dependencies should be // in cc_defaults so that they are included in the tests. "libinputflinger_base", - "libinputreporter", "libinputreader", - "libgui", + "libinputreporter", ], static_libs: [ "libinputdispatcher", @@ -129,6 +148,7 @@ cc_library_shared { cc_library_headers { name: "libinputflinger_headers", + host_supported: true, export_include_dirs: ["include"], } @@ -138,6 +158,7 @@ filegroup { "InputListener.cpp", "InputReaderBase.cpp", "InputThread.cpp", + "NotifyArgs.cpp", "VibrationElement.cpp", ], } @@ -149,18 +170,30 @@ cc_defaults { "libbase", "libbinder", "libcutils", - "libinput", "liblog", - "libui", "libutils", ], header_libs: [ "libinputflinger_headers", ], + target: { + android: { + shared_libs: [ + "libinput", + ], + }, + host: { + static_libs: [ + "libinput", + "libui-types", + ], + }, + }, } cc_library_shared { name: "libinputflinger_base", + host_supported: true, defaults: [ "inputflinger_defaults", "libinputflinger_base_defaults", @@ -169,3 +202,48 @@ cc_library_shared { "libinputflinger_headers", ], } + +// This target will build everything 'input-related'. This could be useful for +// large refactorings of the input code. This is similar to 'm checkbuild', but +// just for input code. +// Use 'm checkinput' to build, and then (optionally) use 'm installclean' to +// remove any of the installed artifacts that you may not want on your actual +// build. +phony { + name: "checkinput", + required: [ + // native targets + "libinput", + "libinputflinger", + "inputflinger_tests", + "inputflinger_benchmarks", + "libinput_tests", + "libpalmrejection_test", + "libandroid_runtime", + "libinputservice_test", + "Bug-115739809", + "StructLayout_test", + + // native fuzzers + "inputflinger_latencytracker_fuzzer", + "inputflinger_cursor_input_fuzzer", + "inputflinger_keyboard_input_fuzzer", + "inputflinger_multitouch_input_fuzzer", + "inputflinger_switch_input_fuzzer", + "inputflinger_input_reader_fuzzer", + "inputflinger_blocking_queue_fuzzer", + "inputflinger_input_classifier_fuzzer", + + // Java/Kotlin targets + "CtsWindowManagerDeviceTestCases", + "InputTests", + "CtsHardwareTestCases", + "CtsInputTestCases", + "CtsViewTestCases", + "CtsWidgetTestCases", + "FrameworksCoreTests", + "FrameworksServicesTests", + "CtsSecurityTestCases", + "CtsSecurityBulletinHostTestCases", + ], +} diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h index 8300e8a3da..fe3728789d 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/services/inputflinger/BlockingQueue.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_BLOCKING_QUEUE_H -#define _UI_INPUT_BLOCKING_QUEUE_H +#pragma once #include "android-base/thread_annotations.h" #include <condition_variable> @@ -106,6 +105,4 @@ private: std::vector<T> mQueue GUARDED_BY(mLock); }; - } // namespace android -#endif diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp index 8aee39fd0b..628ce6fc9a 100644 --- a/services/inputflinger/InputCommonConverter.cpp +++ b/services/inputflinger/InputCommonConverter.cpp @@ -263,6 +263,11 @@ static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) == common static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16); +// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET. +// If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the +// static_assert below and add the new axis here, or leave a comment summarizing your decision. +static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) == + static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET)); static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { common::VideoFrame out; @@ -299,8 +304,8 @@ static void getHalPropertiesAndCoords(const NotifyMotionArgs& args, common::PointerCoords coords; // OK to copy bits because we have static_assert for pointerCoords axes coords.bits = args.pointerCoords[i].bits; - coords.values = std::vector<float>(args.pointerCoords[i].values, - args.pointerCoords[i].values + + coords.values = std::vector<float>(args.pointerCoords[i].values.cbegin(), + args.pointerCoords[i].values.cbegin() + BitSet64::count(args.pointerCoords[i].bits)); outPointerCoords.push_back(coords); } diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index dce327ed18..d33b29888f 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -31,308 +31,34 @@ using android::base::StringPrintf; namespace android { -// --- NotifyConfigurationChangedArgs --- - -NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime) - : NotifyArgs(id, eventTime) {} - -NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs( - const NotifyConfigurationChangedArgs& other) - : NotifyArgs(other.id, other.eventTime) {} - -bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const { - return id == rhs.id && eventTime == rhs.eventTime; -} - -void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const { - listener.notifyConfigurationChanged(this); -} - -// --- NotifyKeyArgs --- - -NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, - int32_t metaState, nsecs_t downTime) - : NotifyArgs(id, eventTime), - deviceId(deviceId), - source(source), - displayId(displayId), - policyFlags(policyFlags), - action(action), - flags(flags), - keyCode(keyCode), - scanCode(scanCode), - metaState(metaState), - downTime(downTime), - readTime(readTime) {} - -NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) - : NotifyArgs(other.id, other.eventTime), - deviceId(other.deviceId), - source(other.source), - displayId(other.displayId), - policyFlags(other.policyFlags), - action(other.action), - flags(other.flags), - keyCode(other.keyCode), - scanCode(other.scanCode), - metaState(other.metaState), - downTime(other.downTime), - readTime(other.readTime) {} - -bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime && - deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && - policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags && - keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState && - downTime == rhs.downTime; -} - -void NotifyKeyArgs::notify(InputListenerInterface& listener) const { - listener.notifyKey(this); -} - -// --- NotifyMotionArgs --- - -NotifyMotionArgs::NotifyMotionArgs( - int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames) - : NotifyArgs(id, eventTime), - deviceId(deviceId), - source(source), - displayId(displayId), - policyFlags(policyFlags), - action(action), - actionButton(actionButton), - flags(flags), - metaState(metaState), - buttonState(buttonState), - classification(classification), - edgeFlags(edgeFlags), - pointerCount(pointerCount), - xPrecision(xPrecision), - yPrecision(yPrecision), - xCursorPosition(xCursorPosition), - yCursorPosition(yCursorPosition), - downTime(downTime), - readTime(readTime), - videoFrames(videoFrames) { - for (uint32_t i = 0; i < pointerCount; i++) { - this->pointerProperties[i].copyFrom(pointerProperties[i]); - this->pointerCoords[i].copyFrom(pointerCoords[i]); - } -} - -NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) - : NotifyArgs(other.id, other.eventTime), - deviceId(other.deviceId), - source(other.source), - displayId(other.displayId), - policyFlags(other.policyFlags), - action(other.action), - actionButton(other.actionButton), - flags(other.flags), - metaState(other.metaState), - buttonState(other.buttonState), - classification(other.classification), - edgeFlags(other.edgeFlags), - pointerCount(other.pointerCount), - xPrecision(other.xPrecision), - yPrecision(other.yPrecision), - xCursorPosition(other.xCursorPosition), - yCursorPosition(other.yCursorPosition), - downTime(other.downTime), - readTime(other.readTime), - videoFrames(other.videoFrames) { - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - } -} - -static inline bool isCursorPositionEqual(float lhs, float rhs) { - return (isnan(lhs) && isnan(rhs)) || lhs == rhs; -} - -bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { - bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime && - deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && - policyFlags == rhs.policyFlags && action == rhs.action && - actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && - buttonState == rhs.buttonState && classification == rhs.classification && - edgeFlags == rhs.edgeFlags && - pointerCount == rhs.pointerCount - // PointerProperties and PointerCoords are compared separately below - && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && - isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) && - isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) && - downTime == rhs.downTime && videoFrames == rhs.videoFrames; - if (!equal) { - return false; - } - - for (size_t i = 0; i < pointerCount; i++) { - equal = - pointerProperties[i] == rhs.pointerProperties[i] - && pointerCoords[i] == rhs.pointerCoords[i]; - if (!equal) { - return false; - } - } - return true; -} - -std::string NotifyMotionArgs::dump() const { - std::string coords; - for (uint32_t i = 0; i < pointerCount; i++) { - if (!coords.empty()) { - coords += ", "; - } - coords += StringPrintf("{%" PRIu32 ": ", i); - coords += - StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id, - pointerCoords[i].getX(), pointerCoords[i].getY(), - pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - const int32_t toolType = pointerProperties[i].toolType; - if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) { - coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType)); - } - const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); - const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); - const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - if (major != 0 || minor != 0) { - coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor, - orientation); - } - coords += "}"; - } - return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 - ", source=%s, action=%s, pointerCount=%" PRIu32 - " pointers=%s, flags=0x%08x)", - id, eventTime, deviceId, inputEventSourceToString(source).c_str(), - MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(), - flags); -} - -void NotifyMotionArgs::notify(InputListenerInterface& listener) const { - listener.notifyMotion(this); -} - -// --- NotifySwitchArgs --- - -NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, - uint32_t switchValues, uint32_t switchMask) - : NotifyArgs(id, eventTime), - policyFlags(policyFlags), - switchValues(switchValues), - switchMask(switchMask) {} - -NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) - : NotifyArgs(other.id, other.eventTime), - policyFlags(other.policyFlags), - switchValues(other.switchValues), - switchMask(other.switchMask) {} - -bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && policyFlags == rhs.policyFlags && - switchValues == rhs.switchValues && switchMask == rhs.switchMask; -} - -void NotifySwitchArgs::notify(InputListenerInterface& listener) const { - listener.notifySwitch(this); -} - -// --- NotifySensorArgs --- - -NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, - InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy, bool accuracyChanged, - nsecs_t hwTimestamp, std::vector<float> values) - : NotifyArgs(id, eventTime), - deviceId(deviceId), - source(source), - sensorType(sensorType), - accuracy(accuracy), - accuracyChanged(accuracyChanged), - hwTimestamp(hwTimestamp), - values(std::move(values)) {} - -NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other) - : NotifyArgs(other.id, other.eventTime), - deviceId(other.deviceId), - source(other.source), - sensorType(other.sensorType), - accuracy(other.accuracy), - accuracyChanged(other.accuracyChanged), - hwTimestamp(other.hwTimestamp), - values(other.values) {} - -bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType && - accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged && - hwTimestamp == rhs.hwTimestamp && values == rhs.values; -} - -void NotifySensorArgs::notify(InputListenerInterface& listener) const { - listener.notifySensor(this); -} - -// --- NotifyVibratorStateArgs --- - -NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, - bool isOn) - : NotifyArgs(id, eventTime), deviceId(deviceId), isOn(isOn) {} - -NotifyVibratorStateArgs::NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) - : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId), isOn(other.isOn) {} - -bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId && - isOn == rhs.isOn; -} - -void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const { - listener.notifyVibratorState(this); -} - -// --- NotifyDeviceResetArgs --- - -NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId) - : NotifyArgs(id, eventTime), deviceId(deviceId) {} - -NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) - : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId) {} - -bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId; -} - -void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const { - listener.notifyDeviceReset(this); -} - -// --- NotifyPointerCaptureChangedArgs --- - -NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs( - int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request) - : NotifyArgs(id, eventTime), request(request) {} - -NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs( - const NotifyPointerCaptureChangedArgs& other) - : NotifyArgs(other.id, other.eventTime), request(other.request) {} - -bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request; -} - -void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const { - listener.notifyPointerCaptureChanged(this); +std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume) { + keep.splice(keep.end(), consume); + return keep; +} + +// --- InputListenerInterface --- + +// Helper to std::visit with lambdas. +template <typename... V> +struct Visitor : V... {}; +// explicit deduction guide (not needed as of C++20) +template <typename... V> +Visitor(V...) -> Visitor<V...>; + +void InputListenerInterface::notify(const NotifyArgs& generalArgs) { + Visitor v{ + [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(&args); }, + [&](const NotifyKeyArgs& args) { notifyKey(&args); }, + [&](const NotifyMotionArgs& args) { notifyMotion(&args); }, + [&](const NotifySwitchArgs& args) { notifySwitch(&args); }, + [&](const NotifySensorArgs& args) { notifySensor(&args); }, + [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(&args); }, + [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(&args); }, + [&](const NotifyPointerCaptureChangedArgs& args) { + notifyPointerCaptureChanged(&args); + }, + }; + std::visit(v, generalArgs); } // --- QueuedInputListener --- @@ -350,47 +76,47 @@ QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener) void QueuedInputListener::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyConfigurationChangedArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyKeyArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyMotionArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifySwitchArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifySensor(const NotifySensorArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifySensorArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyVibratorStateArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyDeviceResetArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { traceEvent(__func__, args->id); - mArgsQueue.emplace_back(std::make_unique<NotifyPointerCaptureChangedArgs>(*args)); + mArgsQueue.emplace_back(*args); } void QueuedInputListener::flush() { - for (const std::unique_ptr<NotifyArgs>& args : mArgsQueue) { - args->notify(mInnerListener); + for (const NotifyArgs& args : mArgsQueue) { + mInnerListener.notify(args); } mArgsQueue.clear(); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 9767cd9b71..9182503692 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -55,14 +55,14 @@ static int32_t exceptionCodeFromStatusT(status_t status) { /** * The event flow is via the "InputListener" interface, as follows: - * InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher + * InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher */ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); - mClassifier = std::make_unique<InputClassifier>(*mDispatcher); - mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier); + mProcessor = std::make_unique<InputProcessor>(*mDispatcher); + mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor); mReader = createInputReader(readerPolicy, *mBlocker); } @@ -110,12 +110,12 @@ InputReaderInterface& InputManager::getReader() { return *mReader; } -UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { +UnwantedInteractionBlockerInterface& InputManager::getBlocker() { return *mBlocker; } -InputClassifierInterface& InputManager::getClassifier() { - return *mClassifier; +InputProcessorInterface& InputManager::getProcessor() { + return *mProcessor; } InputDispatcherInterface& InputManager::getDispatcher() { @@ -125,7 +125,7 @@ InputDispatcherInterface& InputManager::getDispatcher() { void InputManager::monitor() { mReader->monitor(); mBlocker->monitor(); - mClassifier->monitor(); + mProcessor->monitor(); mDispatcher->monitor(); } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 8aad35bf1e..11371934c2 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -14,14 +14,13 @@ * limitations under the License. */ -#ifndef _UI_INPUT_MANAGER_H -#define _UI_INPUT_MANAGER_H +#pragma once /** * Native input manager. */ -#include "InputClassifier.h" +#include "InputProcessor.h" #include "InputReaderBase.h" #include "include/UnwantedInteractionBlockerInterface.h" @@ -52,10 +51,11 @@ class InputDispatcherThread; * this could be a palm on the screen. This stage would alter the event stream to remove either * partially (some of the pointers) or fully (all touches) the unwanted interaction. The events * are processed on the InputReader thread, without any additional queue. The events are then - * posted to the queue managed by the InputClassifier. - * 3. The InputClassifier class starts a thread to communicate with the device-specific - * classifiers. It then waits on the queue of events from UnwantedInteractionBlocker, applies - * a classification to them, and queues them for the InputDispatcher. + * posted to the queue managed by the InputProcessor. + * 3. The InputProcessor class starts a thread to communicate with the device-specific + * IInputProcessor HAL. It then waits on the queue of events from UnwantedInteractionBlocker, + * processes the events (for example, applies a classification to the events), and queues them + * for the InputDispatcher. * 4. The InputDispatcher class starts a thread that waits for new events on the * previous queue and asynchronously dispatches them to applications. * @@ -83,10 +83,10 @@ public: virtual InputReaderInterface& getReader() = 0; /* Gets the unwanted interaction blocker. */ - virtual UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() = 0; + virtual UnwantedInteractionBlockerInterface& getBlocker() = 0; - /* Gets the input classifier */ - virtual InputClassifierInterface& getClassifier() = 0; + /* Gets the input processor */ + virtual InputProcessorInterface& getProcessor() = 0; /* Gets the input dispatcher. */ virtual InputDispatcherInterface& getDispatcher() = 0; @@ -108,8 +108,8 @@ public: status_t stop() override; InputReaderInterface& getReader() override; - UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; - InputClassifierInterface& getClassifier() override; + UnwantedInteractionBlockerInterface& getBlocker() override; + InputProcessorInterface& getProcessor() override; InputDispatcherInterface& getDispatcher() override; void monitor() override; @@ -123,11 +123,9 @@ private: std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker; - std::unique_ptr<InputClassifierInterface> mClassifier; + std::unique_ptr<InputProcessorInterface> mProcessor; std::unique_ptr<InputDispatcherInterface> mDispatcher; }; } // namespace android - -#endif // _UI_INPUT_MANAGER_H diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputProcessor.cpp index 8ce2f35d7b..02d62bffb2 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputProcessor.cpp @@ -14,20 +14,21 @@ * limitations under the License. */ -#define LOG_TAG "InputClassifier" +#define LOG_TAG "InputProcessor" -#include "InputClassifier.h" +#include "InputProcessor.h" #include "InputCommonConverter.h" #include <android-base/stringprintf.h> #include <android/binder_manager.h> #include <android/binder_process.h> +#include <input/Input.h> #include <inttypes.h> #include <log/log.h> #include <algorithm> #include <cmath> #if defined(__linux__) - #include <pthread.h> +#include <pthread.h> #endif #include <unordered_set> @@ -44,10 +45,10 @@ using aidl::android::hardware::input::processor::IInputProcessor; namespace android { -//Max number of elements to store in mEvents. +// Max number of elements to store in mEvents. static constexpr size_t MAX_EVENTS = 5; -template<class K, class V> +template <class K, class V> static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) { auto it = map.find(key); if (it == map.end()) { @@ -123,40 +124,38 @@ ScopedDeathRecipient::~ScopedDeathRecipient() { // --- ClassifierEvent --- -ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) : - type(ClassifierEventType::MOTION), args(std::move(args)) { }; -ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) : - type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { }; -ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) : - type(type), args(std::move(args)) { }; +ClassifierEvent::ClassifierEvent(const NotifyMotionArgs& args) + : type(ClassifierEventType::MOTION), args(args){}; -ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) : - type(other.type), args(std::move(other.args)) { }; +ClassifierEvent::ClassifierEvent(const NotifyDeviceResetArgs& args) + : type(ClassifierEventType::DEVICE_RESET), args(args){}; + +ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args) + : type(type), args(args){}; ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) { type = other.type; - args = std::move(other.args); + args = other.args; return *this; } ClassifierEvent ClassifierEvent::createHalResetEvent() { - return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr); + return ClassifierEvent(ClassifierEventType::HAL_RESET, std::nullopt); } ClassifierEvent ClassifierEvent::createExitEvent() { - return ClassifierEvent(ClassifierEventType::EXIT, nullptr); + return ClassifierEvent(ClassifierEventType::EXIT, std::nullopt); } std::optional<int32_t> ClassifierEvent::getDeviceId() const { switch (type) { case ClassifierEventType::MOTION: { - NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get()); - return motionArgs->deviceId; + const NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*args); + return motionArgs.deviceId; } case ClassifierEventType::DEVICE_RESET: { - NotifyDeviceResetArgs* deviceResetArgs = - static_cast<NotifyDeviceResetArgs*>(args.get()); - return deviceResetArgs->deviceId; + const NotifyDeviceResetArgs& deviceResetArgs = std::get<NotifyDeviceResetArgs>(*args); + return deviceResetArgs.deviceId; } case ClassifierEventType::HAL_RESET: { return std::nullopt; @@ -180,7 +179,7 @@ MotionClassifier::MotionClassifier(std::shared_ptr<IInputProcessor> service) mHalThread = std::thread(&MotionClassifier::processEvents, this); #if defined(__linux__) // Set the thread name for debugging - pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); + pthread_setname_np(mHalThread.native_handle(), "InputProcessor"); #endif } @@ -198,7 +197,7 @@ MotionClassifier::~MotionClassifier() { /** * Obtain the classification from the HAL for a given MotionEvent. - * Should only be called from the InputClassifier thread (mHalThread). + * Should only be called from the InputProcessor thread (mHalThread). * Should not be called from the thread that notifyMotion runs on. * * There is no way to provide a timeout for a HAL call. So if the HAL takes too long @@ -212,12 +211,12 @@ void MotionClassifier::processEvents() { bool halResponseOk = true; switch (event.type) { case ClassifierEventType::MOTION: { - NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get()); - common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(*motionArgs); + NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*event.args); + common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs); common::Classification classification; ndk::ScopedAStatus response = mService->classify(motionEvent, &classification); if (response.isOk()) { - updateClassification(motionArgs->deviceId, motionArgs->eventTime, + updateClassification(motionArgs.deviceId, motionArgs.eventTime, getMotionClassification(classification)); } break; @@ -239,8 +238,8 @@ void MotionClassifier::processEvents() { } } if (!halResponseOk) { - ALOGE("Error communicating with InputClassifier HAL. " - "Exiting MotionClassifier HAL thread"); + ALOGE("Error communicating with InputProcessor HAL. " + "Exiting MotionClassifier HAL thread"); clearClassifications(); return; } @@ -262,14 +261,14 @@ void MotionClassifier::requestExit() { } void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime, - MotionClassification classification) { + MotionClassification classification) { std::scoped_lock lock(mLock); const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0)); if (eventTime < lastDownTime) { // HAL just finished processing an event that belonged to an earlier gesture, // but new gesture is already in progress. Drop this classification. ALOGW("Received late classification. Late by at least %" PRId64 " ms.", - nanoseconds_to_milliseconds(lastDownTime - eventTime)); + nanoseconds_to_milliseconds(lastDownTime - eventTime)); return; } mClassifications[deviceId] = classification; @@ -307,8 +306,7 @@ MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { updateLastDownTime(args.deviceId, args.downTime); } - ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args)); - enqueueEvent(std::move(event)); + enqueueEvent(args); return getClassification(args.deviceId); } @@ -319,66 +317,66 @@ void MotionClassifier::reset() { /** * Per-device reset. Clear the outstanding events that are going to be sent to HAL. - * Request InputClassifier thread to call resetDevice for this particular device. + * Request InputProcessor thread to call resetDevice for this particular device. */ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { int32_t deviceId = args.deviceId; // Clear the pending events right away, to avoid unnecessary work done by the HAL. mEvents.erase([deviceId](const ClassifierEvent& event) { - std::optional<int32_t> eventDeviceId = event.getDeviceId(); - return eventDeviceId && (*eventDeviceId == deviceId); + std::optional<int32_t> eventDeviceId = event.getDeviceId(); + return eventDeviceId && (*eventDeviceId == deviceId); }); - enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args)); -} - -const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) { - if (!mService) { - return "null"; - } - - if (AIBinder_ping(mService->asBinder().get()) == STATUS_OK) { - return "running"; - } - return "not responding"; + enqueueEvent(args); } void MotionClassifier::dump(std::string& dump) { std::scoped_lock lock(mLock); - dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus()); - dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n", - mEvents.size(), MAX_EVENTS); + dump += StringPrintf(INDENT2 "mService connected: %s\n", mService ? "true" : "false"); + dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n", mEvents.size(), MAX_EVENTS); dump += INDENT2 "mClassifications, mLastDownTimes:\n"; dump += INDENT3 "Device Id\tClassification\tLast down time"; // Combine mClassifications and mLastDownTimes into a single table. // Create a superset of device ids. std::unordered_set<int32_t> deviceIds; std::for_each(mClassifications.begin(), mClassifications.end(), - [&deviceIds](auto pair){ deviceIds.insert(pair.first); }); + [&deviceIds](auto pair) { deviceIds.insert(pair.first); }); std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(), - [&deviceIds](auto pair){ deviceIds.insert(pair.first); }); - for(int32_t deviceId : deviceIds) { + [&deviceIds](auto pair) { deviceIds.insert(pair.first); }); + for (int32_t deviceId : deviceIds) { const MotionClassification classification = getValueForKey(mClassifications, deviceId, MotionClassification::NONE); const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0)); - dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64, - deviceId, motionClassificationToString(classification), downTime); + dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64, deviceId, + motionClassificationToString(classification), downTime); } } -// --- InputClassifier --- +void MotionClassifier::monitor() { + std::scoped_lock lock(mLock); + if (mService) { + // Ping the HAL service to ensure it is alive and not blocked. + const binder_status_t status = AIBinder_ping(mService->asBinder().get()); + if (status != STATUS_OK) { + ALOGW("IInputProcessor HAL is not responding; binder ping result: %s", + AStatus_getDescription(AStatus_fromStatus(status))); + } + } +} + +// --- InputProcessor --- -InputClassifier::InputClassifier(InputListenerInterface& listener) : mQueuedListener(listener) {} +InputProcessor::InputProcessor(InputListenerInterface& listener) : mQueuedListener(listener) {} -void InputClassifier::onBinderDied(void* cookie) { - InputClassifier* classifier = static_cast<InputClassifier*>(cookie); - if (classifier == nullptr) { +void InputProcessor::onBinderDied(void* cookie) { + InputProcessor* processor = static_cast<InputProcessor*>(cookie); + if (processor == nullptr) { LOG_ALWAYS_FATAL("Cookie is not valid"); return; } - classifier->setMotionClassifierEnabled(false); + processor->setMotionClassifierEnabled(false); } -void InputClassifier::setMotionClassifierEnabled(bool enabled) { +void InputProcessor::setMotionClassifierEnabled(bool enabled) { std::scoped_lock lock(mLock); if (enabled) { ALOGI("Enabling motion classifier"); @@ -391,7 +389,7 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { * and we can't continue because 'mInitializeMotionClassifier' will block in its * destructor. */ - LOG_ALWAYS_FATAL("The thread to load IInputClassifier is stuck!"); + LOG_ALWAYS_FATAL("The thread to load IInputProcessor is stuck!"); } } mInitializeMotionClassifier = std::async(std::launch::async, [this] { @@ -415,19 +413,19 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { } } -void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { +void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through mQueuedListener.notifyConfigurationChanged(args); mQueuedListener.flush(); } -void InputClassifier::notifyKey(const NotifyKeyArgs* args) { +void InputProcessor::notifyKey(const NotifyKeyArgs* args) { // pass through mQueuedListener.notifyKey(args); mQueuedListener.flush(); } -void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { +void InputProcessor::notifyMotion(const NotifyMotionArgs* args) { { // acquire lock std::scoped_lock lock(mLock); // MotionClassifier is only used for touch events, for now @@ -436,32 +434,38 @@ void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { mQueuedListener.notifyMotion(args); } else { NotifyMotionArgs newArgs(*args); - newArgs.classification = mMotionClassifier->classify(newArgs); + const MotionClassification newClassification = mMotionClassifier->classify(newArgs); + LOG_ALWAYS_FATAL_IF(args->classification != MotionClassification::NONE && + newClassification != MotionClassification::NONE, + "Conflicting classifications %s (new) and %s (old)!", + motionClassificationToString(newClassification), + motionClassificationToString(args->classification)); + newArgs.classification = newClassification; mQueuedListener.notifyMotion(&newArgs); } } // release lock mQueuedListener.flush(); } -void InputClassifier::notifySensor(const NotifySensorArgs* args) { +void InputProcessor::notifySensor(const NotifySensorArgs* args) { // pass through mQueuedListener.notifySensor(args); mQueuedListener.flush(); } -void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) { +void InputProcessor::notifyVibratorState(const NotifyVibratorStateArgs* args) { // pass through mQueuedListener.notifyVibratorState(args); mQueuedListener.flush(); } -void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { +void InputProcessor::notifySwitch(const NotifySwitchArgs* args) { // pass through mQueuedListener.notifySwitch(args); mQueuedListener.flush(); } -void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { +void InputProcessor::notifyDeviceReset(const NotifyDeviceResetArgs* args) { { // acquire lock std::scoped_lock lock(mLock); if (mMotionClassifier) { @@ -474,13 +478,13 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mQueuedListener.flush(); } -void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { +void InputProcessor::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { // pass through mQueuedListener.notifyPointerCaptureChanged(args); mQueuedListener.flush(); } -void InputClassifier::setMotionClassifierLocked( +void InputProcessor::setMotionClassifierLocked( std::unique_ptr<MotionClassifierInterface> motionClassifier) REQUIRES(mLock) { if (motionClassifier == nullptr) { // Destroy the ScopedDeathRecipient object, which will cause it to unlinkToDeath. @@ -490,9 +494,9 @@ void InputClassifier::setMotionClassifierLocked( mMotionClassifier = std::move(motionClassifier); } -void InputClassifier::dump(std::string& dump) { +void InputProcessor::dump(std::string& dump) { std::scoped_lock lock(mLock); - dump += "Input Classifier State:\n"; + dump += "Input Processor State:\n"; dump += INDENT1 "Motion Classifier:\n"; if (mMotionClassifier) { mMotionClassifier->dump(dump); @@ -502,11 +506,11 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } -void InputClassifier::monitor() { +void InputProcessor::monitor() { std::scoped_lock lock(mLock); + if (mMotionClassifier) mMotionClassifier->monitor(); } -InputClassifier::~InputClassifier() { -} +InputProcessor::~InputProcessor() {} } // namespace android diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputProcessor.h index 56cf760256..f4d02b6f30 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputProcessor.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_CLASSIFIER_H -#define _UI_INPUT_CLASSIFIER_H +#pragma once #include <android-base/thread_annotations.h> #include <future> @@ -36,12 +35,12 @@ enum class ClassifierEventType : uint8_t { struct ClassifierEvent { ClassifierEventType type; - std::unique_ptr<NotifyArgs> args; + std::optional<NotifyArgs> args; - ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args); - ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args); - ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args); - ClassifierEvent(ClassifierEvent&& other); + ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args); + ClassifierEvent(const NotifyMotionArgs& args); + ClassifierEvent(const NotifyDeviceResetArgs& args); + ClassifierEvent(ClassifierEvent&& other) = default; ClassifierEvent& operator=(ClassifierEvent&& other); // Convenience function to create a HAL_RESET event @@ -61,8 +60,8 @@ struct ClassifierEvent { */ class MotionClassifierInterface { public: - MotionClassifierInterface() { } - virtual ~MotionClassifierInterface() { } + MotionClassifierInterface() {} + virtual ~MotionClassifierInterface() {} /** * Based on the motion event described by NotifyMotionArgs, * provide a MotionClassification for the current gesture. @@ -78,16 +77,21 @@ public: virtual void reset(const NotifyDeviceResetArgs& args) = 0; /** - * Dump the state of the motion classifier + * Dump the state of the motion classifier. */ virtual void dump(std::string& dump) = 0; + + /** + * Called by the heartbeat to ensure the HAL is still processing normally. + */ + virtual void monitor() = 0; }; /** * Base interface for an InputListener stage. * Provides classification to events. */ -class InputClassifierInterface : public InputListenerInterface { +class InputProcessorInterface : public InputListenerInterface { public: virtual void setMotionClassifierEnabled(bool enabled) = 0; /** @@ -96,11 +100,11 @@ public: */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the classifier has not deadlocked. */ + /** Called by the heartbeat to ensure that the classifier has not deadlocked. */ virtual void monitor() = 0; - InputClassifierInterface() { } - virtual ~InputClassifierInterface() { } + InputProcessorInterface() {} + virtual ~InputProcessorInterface() {} }; // --- Implementations --- @@ -119,10 +123,10 @@ private: }; /** - * Implementation of MotionClassifierInterface that calls the InputClassifier HAL + * Implementation of MotionClassifierInterface that calls the InputProcessor HAL * in order to determine the classification for the current gesture. * - * The InputClassifier HAL may keep track of the entire gesture in order to determine + * The InputProcessor HAL may keep track of the entire gesture in order to determine * the classification, and may be hardware-specific. It may use the data in * NotifyMotionArgs::videoFrames field to drive the classification decisions. * The HAL is called from a separate thread. @@ -155,33 +159,34 @@ public: virtual void reset(const NotifyDeviceResetArgs& args) override; virtual void dump(std::string& dump) override; + virtual void monitor() override; private: friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation explicit MotionClassifier( std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service); - // The events that need to be sent to the HAL. + /** 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, + * Thread that will communicate with InputProcessor HAL. + * This should be the only thread that communicates with InputProcessor HAL, * because this thread is allowed to block on the HAL calls. */ std::thread mHalThread; /** - * Process events and call the InputClassifier HAL + * Process events and call the InputProcessor HAL */ void processEvents(); /** * Access to the InputProcessor HAL. May be null if init() hasn't completed yet. * When init() successfully completes, mService is guaranteed to remain non-null and to not * change its value until MotionClassifier is destroyed. - * This variable is *not* guarded by mLock in the InputClassifier thread, because + * This variable is *not* guarded by mLock in the InputProcessor thread, because * that thread knows exactly when this variable is initialized. * When accessed in any other thread, mService is checked for nullness with a lock. */ @@ -191,8 +196,8 @@ private: * Per-device input classifications. Should only be accessed using the * getClassification / setClassification methods. */ - std::unordered_map<int32_t /*deviceId*/, MotionClassification> - mClassifications GUARDED_BY(mLock); + std::unordered_map<int32_t /*deviceId*/, MotionClassification> mClassifications + GUARDED_BY(mLock); /** * Set the current classification for a given device. */ @@ -202,7 +207,7 @@ private: */ MotionClassification getClassification(int32_t deviceId); void updateClassification(int32_t deviceId, nsecs_t eventTime, - MotionClassification classification); + MotionClassification classification); /** * Clear all current classifications */ @@ -211,7 +216,7 @@ private: * Per-device times when the last ACTION_DOWN was received. * Used to reject late classifications that do not belong to the current gesture. * - * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion. + * Accessed indirectly by both InputProcessor thread and the thread that receives notifyMotion. */ std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock); @@ -220,7 +225,7 @@ private: void clearDeviceState(int32_t deviceId); /** - * Exit the InputClassifier HAL thread. + * Exit the InputProcessor HAL thread. * Useful for tests to ensure proper cleanup. */ void requestExit(); @@ -231,14 +236,14 @@ private: }; /** - * Implementation of the InputClassifierInterface. + * Implementation of the InputProcessorInterface. * 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 InputProcessor : public InputProcessorInterface { public: - explicit InputClassifier(InputListenerInterface& listener); + explicit InputProcessor(InputListenerInterface& listener); void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; void notifyKey(const NotifyKeyArgs* args) override; @@ -252,7 +257,7 @@ public: void dump(std::string& dump) override; void monitor() override; - ~InputClassifier(); + ~InputProcessor(); // Called from InputManager void setMotionClassifierEnabled(bool enabled) override; @@ -282,4 +287,3 @@ private: }; } // namespace android -#endif diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp index b87f7a1243..e74f258168 100644 --- a/services/inputflinger/InputThread.cpp +++ b/services/inputflinger/InputThread.cpp @@ -41,7 +41,7 @@ private: InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake) : mName(name), mThreadWake(wake) { - mThread = new InputThreadImpl(loop); + mThread = sp<InputThreadImpl>::make(loop); mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); } @@ -54,7 +54,13 @@ InputThread::~InputThread() { } bool InputThread::isCallingThread() { +#if defined(__ANDROID__) return gettid() == mThread->getTid(); +#else + // Assume that the caller is doing everything correctly, + // since thread information is not available on host + return false; +#endif } } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp new file mode 100644 index 0000000000..b192ad73c3 --- /dev/null +++ b/services/inputflinger/NotifyArgs.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NotifyArgs" + +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include "NotifyArgs.h" + +#include <android-base/stringprintf.h> +#include <android/log.h> +#include <math.h> +#include <utils/Trace.h> + +using android::base::StringPrintf; + +namespace android { + +// --- NotifyConfigurationChangedArgs --- + +NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime) + : id(id), eventTime(eventTime) {} + +// --- NotifyKeyArgs --- + +NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, + uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, nsecs_t downTime) + : id(id), + eventTime(eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + flags(flags), + keyCode(keyCode), + scanCode(scanCode), + metaState(metaState), + downTime(downTime), + readTime(readTime) {} + +// --- NotifyMotionArgs --- + +NotifyMotionArgs::NotifyMotionArgs( + int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames) + : id(id), + eventTime(eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + pointerCount(pointerCount), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), + readTime(readTime), + videoFrames(videoFrames) { + for (uint32_t i = 0; i < pointerCount; i++) { + this->pointerProperties[i].copyFrom(pointerProperties[i]); + this->pointerCoords[i].copyFrom(pointerCoords[i]); + } +} + +NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) + : id(other.id), + eventTime(other.eventTime), + deviceId(other.deviceId), + source(other.source), + displayId(other.displayId), + policyFlags(other.policyFlags), + action(other.action), + actionButton(other.actionButton), + flags(other.flags), + metaState(other.metaState), + buttonState(other.buttonState), + classification(other.classification), + edgeFlags(other.edgeFlags), + pointerCount(other.pointerCount), + xPrecision(other.xPrecision), + yPrecision(other.yPrecision), + xCursorPosition(other.xCursorPosition), + yCursorPosition(other.yCursorPosition), + downTime(other.downTime), + readTime(other.readTime), + videoFrames(other.videoFrames) { + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(other.pointerProperties[i]); + pointerCoords[i].copyFrom(other.pointerCoords[i]); + } +} + +static inline bool isCursorPositionEqual(float lhs, float rhs) { + return (isnan(lhs) && isnan(rhs)) || lhs == rhs; +} + +bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { + bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime && + deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && + policyFlags == rhs.policyFlags && action == rhs.action && + actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && + buttonState == rhs.buttonState && classification == rhs.classification && + edgeFlags == rhs.edgeFlags && + pointerCount == rhs.pointerCount + // PointerProperties and PointerCoords are compared separately below + && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && + isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) && + isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) && + downTime == rhs.downTime && videoFrames == rhs.videoFrames; + if (!equal) { + return false; + } + + for (size_t i = 0; i < pointerCount; i++) { + equal = pointerProperties[i] == rhs.pointerProperties[i] && + pointerCoords[i] == rhs.pointerCoords[i]; + if (!equal) { + return false; + } + } + return true; +} + +std::string NotifyMotionArgs::dump() const { + std::string coords; + for (uint32_t i = 0; i < pointerCount; i++) { + if (!coords.empty()) { + coords += ", "; + } + coords += StringPrintf("{%" PRIu32 ": ", i); + coords += + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id, + pointerCoords[i].getX(), pointerCoords[i].getY(), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + const int32_t toolType = pointerProperties[i].toolType; + if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) { + coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType)); + } + const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + if (major != 0 || minor != 0) { + coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor, + orientation); + } + coords += "}"; + } + return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 + ", source=%s, action=%s, pointerCount=%" PRIu32 + " pointers=%s, flags=0x%08x)", + id, eventTime, deviceId, inputEventSourceToString(source).c_str(), + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(), + flags); +} + +// --- NotifySwitchArgs --- + +NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, + uint32_t switchValues, uint32_t switchMask) + : id(id), + eventTime(eventTime), + policyFlags(policyFlags), + switchValues(switchValues), + switchMask(switchMask) {} + +// --- NotifySensorArgs --- + +NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, bool accuracyChanged, + nsecs_t hwTimestamp, std::vector<float> values) + : id(id), + eventTime(eventTime), + deviceId(deviceId), + source(source), + sensorType(sensorType), + accuracy(accuracy), + accuracyChanged(accuracyChanged), + hwTimestamp(hwTimestamp), + values(std::move(values)) {} + +// --- NotifyVibratorStateArgs --- + +NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, + bool isOn) + : id(id), eventTime(eventTime), deviceId(deviceId), isOn(isOn) {} + +// --- NotifyDeviceResetArgs --- + +NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId) + : id(id), eventTime(eventTime), deviceId(deviceId) {} + +// --- NotifyPointerCaptureChangedArgs --- + +NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs( + int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request) + : id(id), eventTime(eventTime), request(request) {} + +// Helper to std::visit with lambdas. +template <typename... V> +struct Visitor : V... {}; +// explicit deduction guide (not needed as of C++20) +template <typename... V> +Visitor(V...) -> Visitor<V...>; + +const char* toString(const NotifyArgs& args) { + Visitor toStringVisitor{ + [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; }, + [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; }, + [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; }, + [&](const NotifySensorArgs&) { return "NotifySensorArgs"; }, + [&](const NotifySwitchArgs&) { return "NotifySwitchArgs"; }, + [&](const NotifyDeviceResetArgs&) { return "NotifyDeviceResetArgs"; }, + [&](const NotifyPointerCaptureChangedArgs&) { + return "NotifyPointerCaptureChangedArgs"; + }, + [&](const NotifyVibratorStateArgs&) { return "NotifyVibratorStateArgs"; }, + }; + return std::visit(toStringVisitor, args); +} + +} // namespace android diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp index beec2e162e..ddd514676b 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp +++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp @@ -25,8 +25,7 @@ static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) { for (size_t i = 0; i < args.pointerCount; i++) { // Make sure we are canceling stylus pointers const int32_t toolType = args.pointerProperties[i].toolType; - if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(toolType)) { hasStylus = true; } if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 8d5d8839e8..3d7242e9ea 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -41,13 +41,111 @@ "name": "CtsViewTestCases", "options": [ { + "include-filter": "android.view.cts.input", "include-filter": "android.view.cts.MotionEventTest", "include-filter": "android.view.cts.PointerCaptureTest", + "include-filter": "android.view.cts.TooltipTest", "include-filter": "android.view.cts.VerifyInputEventTest" } ] }, { + "name": "CtsWidgetTestCases", + "options": [ + { + "include-filter": "android.widget.cts.NumberPickerTest" + } + ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.view.VerifiedKeyEventTest", + "include-filter": "android.view.VerifiedMotionEventTest" + } + ] + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.input" + } + ] + }, + { + "name": "CtsSecurityTestCases", + "options": [ + { + "include-filter": "android.security.cts.MotionEventTest" + } + ] + }, + { + "name": "CtsSecurityBulletinHostTestCases", + "options": [ + { + "include-filter": "android.security.cts.Poc19_03#testPocBug_115739809" + } + ] + } + ], + "hwasan-postsubmit": [ + { + "name": "CtsWindowManagerDeviceTestCases", + "options": [ + { + "include-filter": "android.server.wm.WindowInputTests" + } + ] + }, + { + "name": "libinput_tests" + }, + { + "name": "inputflinger_tests" + }, + { + "name": "libpalmrejection_test" + }, + { + "name": "InputTests" + }, + { + "name": "libinputservice_test" + }, + { + "name": "CtsHardwareTestCases", + "options": [ + { + "include-filter": "android.hardware.input.cts.tests" + } + ] + }, + { + "name": "CtsInputTestCases" + }, + { + "name": "CtsViewTestCases", + "options": [ + { + "include-filter": "android.view.cts.MotionEventTest", + "include-filter": "android.view.cts.PointerCaptureTest", + "include-filter": "android.view.cts.TooltipTest", + "include-filter": "android.view.cts.VerifyInputEventTest" + } + ] + }, + { + "name": "CtsWidgetTestCases", + "options": [ + { + "include-filter": "android.widget.cts.NumberPickerTest" + } + ] + }, + { "name": "FrameworksCoreTests", "options": [ { diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index ec41025e9d..c170b81475 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -99,14 +99,17 @@ static bool isPalmRejectionEnabled() { } static int getLinuxToolCode(int toolType) { - if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { - return BTN_TOOL_PEN; + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return BTN_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_ERASER: + return BTN_TOOL_RUBBER; + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return BTN_TOOL_FINGER; + default: + ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType); + return BTN_TOOL_FINGER; } - if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { - return BTN_TOOL_FINGER; - } - ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType); - return BTN_TOOL_FINGER; } static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { @@ -195,7 +198,7 @@ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) { std::set<int32_t> stylusPointerIds; for (uint32_t i = 0; i < args.pointerCount; i++) { - if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { + if (isStylusToolType(args.pointerProperties[i].toolType)) { stylusPointerIds.insert(args.pointerProperties[i].id); } } diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index 75071d55ac..4e2a6fbf87 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -21,12 +21,10 @@ cc_benchmark { "libbinder", "libcrypto", "libcutils", - "libinput", "libinputflinger_base", "libinputreporter", "liblog", "libstatslog", - "libui", "libutils", ], static_libs: [ diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index a2e60c4e6f..3d4dd7eabb 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -82,8 +82,6 @@ private: void notifyVibratorState(int32_t deviceId, bool isOn) override {} - void notifyUntrustedTouch(const std::string& obscuringPackage) override {} - void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { *outConfig = mConfig; } @@ -260,14 +258,15 @@ static NotifyMotionArgs generateMotionArgs() { static void benchmarkNotifyMotion(benchmark::State& state) { // Create dispatcher - sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy(); + sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make(); InputDispatcher dispatcher(fakePolicy); dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher.start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window"); dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -294,14 +293,15 @@ static void benchmarkNotifyMotion(benchmark::State& state) { static void benchmarkInjectMotion(benchmark::State& state) { // Create dispatcher - sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy(); + sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make(); InputDispatcher dispatcher(fakePolicy); dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher.start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window"); dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -327,14 +327,15 @@ static void benchmarkInjectMotion(benchmark::State& state) { static void benchmarkOnWindowInfosChanged(benchmark::State& state) { // Create dispatcher - sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy(); + sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make(); InputDispatcher dispatcher(fakePolicy); dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher.start(); // Create a window std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window"); std::vector<gui::WindowInfo> windowInfos{*window->getInfo()}; gui::DisplayInfo info; diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index cdad9c93fd..ab5c5ef99c 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -23,6 +23,7 @@ package { cc_library_headers { name: "libinputdispatcher_headers", + host_supported: true, export_include_dirs: [ "include", ], @@ -33,6 +34,7 @@ filegroup { srcs: [ "AnrTracker.cpp", "Connection.cpp", + "DragState.cpp", "Entry.cpp", "FocusResolver.cpp", "InjectionState.cpp", @@ -44,8 +46,8 @@ filegroup { "LatencyAggregator.cpp", "LatencyTracker.cpp", "Monitor.cpp", + "TouchedWindow.cpp", "TouchState.cpp", - "DragState.cpp", ], } @@ -56,21 +58,34 @@ cc_defaults { "libbase", "libcrypto", "libcutils", - "libinput", "libkll", "liblog", "libprotobuf-cpp-lite", "libstatslog", - "libstatspull", - "libstatssocket", - "libui", - "libgui", "libutils", "server_configurable_flags", ], static_libs: [ "libattestation", + "libgui_window_info_static", ], + target: { + android: { + shared_libs: [ + "libgui", + "libinput", + "libstatspull", + "libstatssocket", + ], + }, + host: { + static_libs: [ + "libinput", + "libstatspull", + "libstatssocket", + ], + }, + }, header_libs: [ "libinputdispatcher_headers", ], @@ -85,8 +100,8 @@ cc_library_static { shared_libs: [ // This should consist only of dependencies from inputflinger. Other dependencies should be // in cc_defaults so that they are included in the tests. - "libinputreporter", "libinputflinger_base", + "libinputreporter", ], export_header_lib_headers: [ "libinputdispatcher_headers", diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp index c3f611e7db..a18063f299 100644 --- a/services/inputflinger/dispatcher/AnrTracker.cpp +++ b/services/inputflinger/dispatcher/AnrTracker.cpp @@ -54,7 +54,7 @@ bool AnrTracker::empty() const { } // If empty() is false, return the time at which the next connection should cause an ANR -// If empty() is true, return LONG_LONG_MAX +// If empty() is true, return LLONG_MAX nsecs_t AnrTracker::firstTimeout() const { if (mAnrTimeouts.empty()) { return std::numeric_limits<nsecs_t>::max(); diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h index 097dba5bea..cff5d00e4b 100644 --- a/services/inputflinger/dispatcher/AnrTracker.h +++ b/services/inputflinger/dispatcher/AnrTracker.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H -#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H +#pragma once #include <binder/IBinder.h> #include <utils/Timers.h> @@ -36,7 +35,7 @@ public: bool empty() const; // If empty() is false, return the time at which the next connection should cause an ANR - // If empty() is true, return LONG_LONG_MAX + // If empty() is true, return LLONG_MAX nsecs_t firstTimeout() const; // Return the token of the next connection that should cause an ANR. // Do not call this unless empty() is false, you will encounter undefined behaviour. @@ -56,5 +55,3 @@ private: }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h index 99e2108dbf..512cb6e692 100644 --- a/services/inputflinger/dispatcher/CancelationOptions.h +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -14,16 +14,17 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H -#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H +#pragma once +#include <utils/BitSet.h> #include <optional> -namespace android::inputdispatcher { +namespace android { +namespace inputdispatcher { /* Specifies which events are to be canceled and why. */ struct CancelationOptions { - enum Mode { + enum class Mode { CANCEL_ALL_EVENTS = 0, CANCEL_POINTER_EVENTS = 1, CANCEL_NON_POINTER_EVENTS = 2, @@ -45,9 +46,11 @@ struct CancelationOptions { // The specific display id of events to cancel, or nullopt to cancel events on any display. std::optional<int32_t> displayId = std::nullopt; + // The specific pointers to cancel, or nullopt to cancel all pointer events + std::optional<BitSet32> pointerIds = std::nullopt; + CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} }; -} // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H +} // namespace inputdispatcher +} // namespace android diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index dc6a081ff6..6040e9b078 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H -#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H +#pragma once #include "InputState.h" @@ -74,5 +73,3 @@ public: }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index 9a2aea69ec..d2ad40754a 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H -#define _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H +#pragma once #define LOG_TAG "InputDispatcher" @@ -92,5 +91,3 @@ const bool DEBUG_APP_SWITCH = const bool DEBUG_HOVER = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO); } // namespace android::inputdispatcher - -#endif // _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h index d1c8b8a10e..9809148853 100644 --- a/services/inputflinger/dispatcher/DragState.h +++ b/services/inputflinger/dispatcher/DragState.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H -#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H +#pragma once #include <gui/WindowInfo.h> #include <utils/StrongPointer.h> @@ -44,5 +43,3 @@ struct DragState { } // namespace inputdispatcher } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 8046bbe25c..7bbfb95b88 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -166,7 +166,7 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou repeatCount(repeatCount), downTime(downTime), syntheticRepeat(false), - interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), + interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN), interceptKeyWakeupTime(0) {} KeyEntry::~KeyEntry() {} @@ -189,15 +189,16 @@ void KeyEntry::recycle() { dispatchInProgress = false; syntheticRepeat = false; - interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; interceptKeyWakeupTime = 0; } // --- TouchModeEntry --- -TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode) +TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId) : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER), - inTouchMode(inTouchMode) {} + inTouchMode(inTouchMode), + displayId(displayId) {} TouchModeEntry::~TouchModeEntry() {} @@ -307,7 +308,8 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; -DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, +DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, const ui::Transform& rawTransform, float globalScaleFactor) : seq(nextSeq()), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 0f792967eb..3799814f67 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H -#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H +#pragma once #include "InjectionState.h" #include "InputTarget.h" @@ -141,11 +140,11 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats - enum InterceptKeyResult { - INTERCEPT_KEY_RESULT_UNKNOWN, - INTERCEPT_KEY_RESULT_SKIP, - INTERCEPT_KEY_RESULT_CONTINUE, - INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, + enum class InterceptKeyResult { + UNKNOWN, + SKIP, + CONTINUE, + TRY_AGAIN_LATER, }; InterceptKeyResult interceptKeyResult; // set based on the interception result nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER @@ -211,8 +210,9 @@ struct SensorEntry : EventEntry { struct TouchModeEntry : EventEntry { bool inTouchMode; + int32_t displayId; - TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode); + TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId); std::string getDescription() const override; ~TouchModeEntry() override; @@ -223,7 +223,7 @@ struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 std::shared_ptr<EventEntry> eventEntry; // the event to dispatch - int32_t targetFlags; + ftl::Flags<InputTarget::Flags> targetFlags; ui::Transform transform; ui::Transform rawTransform; float globalScaleFactor; @@ -238,13 +238,15 @@ struct DispatchEntry { int32_t resolvedAction; int32_t resolvedFlags; - DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - const ui::Transform& transform, const ui::Transform& rawTransform, - float globalScaleFactor); + DispatchEntry(std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, + const ui::Transform& rawTransform, float globalScaleFactor); - inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } + inline bool hasForegroundTarget() const { + return targetFlags.test(InputTarget::Flags::FOREGROUND); + } - inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); } private: static volatile int32_t sNextSeqAtomic; @@ -257,5 +259,3 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, const ui::Transform& rawTransform); } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp index a02b3e8779..4da846bcf8 100644 --- a/services/inputflinger/dispatcher/FocusResolver.cpp +++ b/services/inputflinger/dispatcher/FocusResolver.cpp @@ -13,24 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "FocusResolver" +#define LOG_TAG "InputDispatcher" #define ATRACE_TAG ATRACE_TAG_INPUT #define INDENT " " #define INDENT2 " " -// Log debug messages about input focus tracking. -static constexpr bool DEBUG_FOCUS = false; - #include <inttypes.h> #include <android-base/stringprintf.h> #include <binder/Binder.h> #include <ftl/enum.h> #include <gui/WindowInfo.h> -#include <log/log.h> +#include "DebugConfig.h" #include "FocusResolver.h" using android::gui::FocusRequest; diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h index 90cf150ac3..d9e27ba50b 100644 --- a/services/inputflinger/dispatcher/InjectionState.h +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H -#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H +#pragma once #include <stdint.h> #include "InputDispatcherInterface.h" @@ -41,5 +40,3 @@ private: } // namespace inputdispatcher } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index eb97a68a9a..4aac377b42 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -25,8 +25,11 @@ #include <android/os/IInputConstants.h> #include <binder/Binder.h> #include <ftl/enum.h> +#if defined(__ANDROID__) #include <gui/SurfaceComposerClient.h> +#endif #include <input/InputDevice.h> +#include <input/PrintTools.h> #include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> @@ -48,6 +51,7 @@ #define INDENT3 " " #define INDENT4 " " +using namespace android::ftl::flag_operators; using android::base::HwTimeoutMultiplier; using android::base::Result; using android::base::StringPrintf; @@ -56,8 +60,6 @@ using android::gui::FocusRequest; using android::gui::TouchOcclusionMode; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; -using android::os::BlockUntrustedTouchesMode; -using android::os::IInputConstants; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; @@ -146,21 +148,23 @@ bool validateKeyEvent(int32_t action) { } bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { - switch (action & AMOTION_EVENT_ACTION_MASK) { + switch (MotionEvent::getActionMasked(action)) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: + return pointerCount == 1; case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_OUTSIDE: case AMOTION_EVENT_ACTION_HOVER_ENTER: case AMOTION_EVENT_ACTION_HOVER_MOVE: case AMOTION_EVENT_ACTION_HOVER_EXIT: + return pointerCount >= 1; + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_OUTSIDE: case AMOTION_EVENT_ACTION_SCROLL: return true; case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t index = getMotionEventActionPointerIndex(action); - return index >= 0 && index < pointerCount; + const int32_t index = MotionEvent::getActionIndex(action); + return index >= 0 && index < pointerCount && pointerCount > 1; } case AMOTION_EVENT_ACTION_BUTTON_PRESS: case AMOTION_EVENT_ACTION_BUTTON_RELEASE: @@ -240,9 +244,9 @@ std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTi } dump.append(INDENT4); dump += entry.eventEntry->getDescription(); - dump += StringPrintf(", seq=%" PRIu32 - ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms", - entry.seq, entry.targetFlags, entry.resolvedAction, + dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, resolvedAction=%d, age=%" PRId64 + "ms", + entry.seq, entry.targetFlags.string().c_str(), entry.resolvedAction, ns2ms(currentTime - entry.eventEntry->eventTime)); if (entry.deliveryTime != 0) { // This entry was delivered, so add information on how long we've been waiting @@ -287,9 +291,9 @@ bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) first->applicationInfo.token == second->applicationInfo.token; } -std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, - std::shared_ptr<EventEntry> eventEntry, - int32_t inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry( + const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> inputTargetFlags) { if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, @@ -478,15 +482,14 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32 bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && - (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); + isStylusToolType(entry.pointerProperties[pointerIndex].toolType); } -// Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND. +// Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND. // Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to // such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can // be sent to such a window, but it is not a foreground event and doesn't use -// InputTarget::FLAG_FOREGROUND. +// InputTarget::Flags::FOREGROUND. bool canReceiveForegroundTouches(const WindowInfo& info) { // A non-touchable window can still receive touch events (e.g. in the case of // STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches. @@ -525,6 +528,32 @@ std::optional<std::string> verifyTargetedInjection(const sp<WindowInfoHandle>& w return {}; } +Point resolveTouchedPosition(const MotionEntry& entry) { + const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); + // Always dispatch mouse events to cursor position. + if (isFromMouse) { + return Point(static_cast<int32_t>(entry.xCursorPosition), + static_cast<int32_t>(entry.yCursorPosition)); + } + + const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action); + return Point(static_cast<int32_t>( + entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)), + static_cast<int32_t>( + entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y))); +} + +std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) { + if (eventEntry.type == EventEntry::Type::KEY) { + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry); + return keyEntry.downTime; + } else if (eventEntry.type == EventEntry::Type::MOTION) { + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); + return motionEntry.downTime; + } + return std::nullopt; +} + } // namespace // --- InputDispatcher --- @@ -539,30 +568,26 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mAppSwitchSawKeyDown(false), - mAppSwitchDueTime(LONG_LONG_MAX), + mAppSwitchDueTime(LLONG_MAX), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), - // mInTouchMode will be initialized by the WindowManager to the default device config. - // To avoid leaking stack in case that call never comes, and for tests, - // initialize it here anyways. - mInTouchMode(kDefaultInTouchMode), mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mWindowTokenWithPointerCapture(nullptr), mStaleEventTimeout(staleEventTimeout), mLatencyAggregator(), mLatencyTracker(&mLatencyAggregator) { - mLooper = new Looper(false); + mLooper = sp<Looper>::make(false); mReporter = createInputReporter(); - mWindowInfoListener = new DispatcherWindowListener(*this); + mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); +#if defined(__ANDROID__) SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); - +#endif mKeyRepeatState.lastKeyEntry = nullptr; - policy->getDispatcherConfiguration(&mConfig); } @@ -600,7 +625,7 @@ status_t InputDispatcher::stop() { } void InputDispatcher::dispatchOnce() { - nsecs_t nextWakeupTime = LONG_LONG_MAX; + nsecs_t nextWakeupTime = LLONG_MAX; { // acquire lock std::scoped_lock _l(mLock); mDispatcherIsAlive.notify_all(); @@ -614,7 +639,7 @@ void InputDispatcher::dispatchOnce() { // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptable()) { - nextWakeupTime = LONG_LONG_MIN; + nextWakeupTime = LLONG_MIN; } // If we are still waiting for ack on some events, @@ -624,7 +649,7 @@ void InputDispatcher::dispatchOnce() { // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events - if (nextWakeupTime == LONG_LONG_MAX) { + if (nextWakeupTime == LLONG_MAX) { mDispatcherEnteredIdle.notify_all(); } } // release lock @@ -669,14 +694,14 @@ void InputDispatcher::processNoFocusedWindowAnrLocked() { */ nsecs_t InputDispatcher::processAnrsLocked() { const nsecs_t currentTime = now(); - nsecs_t nextAnrCheck = LONG_LONG_MAX; + nsecs_t nextAnrCheck = LLONG_MAX; // Check if we are waiting for a focused window to appear. Raise ANR if waited too long if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { if (currentTime >= *mNoFocusedWindowTimeoutTime) { processNoFocusedWindowAnrLocked(); mAwaitedFocusedApplication.reset(); mNoFocusedWindowTimeoutTime = std::nullopt; - return LONG_LONG_MIN; + return LLONG_MIN; } else { // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes. nextAnrCheck = *mNoFocusedWindowTimeoutTime; @@ -699,7 +724,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { // Stop waking up for this unresponsive connection mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); onAnrLocked(connection); - return LONG_LONG_MIN; + return LLONG_MIN; } std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( @@ -906,7 +931,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { mLastDropReason = dropReason; releasePendingEventLocked(); - *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately } } @@ -927,13 +952,10 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt // If the application takes too long to catch up then we drop all events preceding // the touch into the other window. if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) { - int32_t displayId = motionEntry.displayId; - int32_t x = static_cast<int32_t>( - motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = static_cast<int32_t>( - motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - + const int32_t displayId = motionEntry.displayId; + const auto [x, y] = resolveTouchedPosition(motionEntry); const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/); + sp<WindowInfoHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus); if (touchedWindowHandle != nullptr && @@ -1012,8 +1034,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent); if (pendingKey.keyCode == keyEntry.keyCode && pendingKey.interceptKeyResult == - KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { - pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { + pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; pendingKey.interceptKeyWakeupTime = 0; needWake = true; } @@ -1062,7 +1084,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI int32_t y, TouchState* touchState, bool isStylus, bool addOutsideTargets, - bool ignoreDragWindow) { + bool ignoreDragWindow) const { if (addOutsideTargets && touchState == nullptr) { LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets"); } @@ -1080,7 +1102,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI if (addOutsideTargets && info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { - touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, + touchState->addOrUpdateWindow(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, BitSet32(0)); } } @@ -1148,17 +1170,18 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason switch (entry.type) { case EventEntry::Type::KEY: { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); break; } case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry); if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); } else { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + reason); synthesizeCancelationEventsForAllConnectionsLocked(options); } break; @@ -1192,11 +1215,11 @@ bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) { } bool InputDispatcher::isAppSwitchPendingLocked() { - return mAppSwitchDueTime != LONG_LONG_MAX; + return mAppSwitchDueTime != LLONG_MAX; } void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { - mAppSwitchDueTime = LONG_LONG_MAX; + mAppSwitchDueTime = LLONG_MAX; if (DEBUG_APP_SWITCH) { if (handled) { @@ -1313,7 +1336,7 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, resetKeyRepeatLocked(); } - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset"); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset"); options.deviceId = entry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); return true; @@ -1351,7 +1374,7 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<F } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + channel->getName(); @@ -1425,7 +1448,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); @@ -1435,7 +1458,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime, const std::shared_ptr<TouchModeEntry>& entry) { const std::vector<sp<WindowInfoHandle>>& windowHandles = - getWindowHandlesLocked(mFocusedDisplayId); + getWindowHandlesLocked(entry->displayId); if (windowHandles.empty()) { return; } @@ -1452,7 +1475,6 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked const std::vector<sp<WindowInfoHandle>>& windowHandles) const { std::vector<InputTarget> inputTargets; for (const sp<WindowInfoHandle>& handle : windowHandles) { - // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only. const sp<IBinder>& token = handle->getToken(); if (token == nullptr) { continue; @@ -1463,7 +1485,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; inputTargets.push_back(target); } return inputTargets; @@ -1490,7 +1512,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key // stop the key repeat on current device. entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves + mKeyRepeatState.nextRepeatTime = LLONG_MAX; // don't generate repeats ourselves } else { // Not a repeat. Save key down state in case we do see a repeat later. resetKeyRepeatLocked(); @@ -1519,19 +1541,19 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } // Handle case where the policy asked us to try again later last time. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { if (entry->interceptKeyWakeupTime < *nextWakeupTime) { *nextWakeupTime = entry->interceptKeyWakeupTime; } return false; // wait until next wakeup } - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; entry->interceptKeyWakeupTime = 0; } // Give the policy a chance to intercept the key. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { sp<IBinder> focusedWindowToken = mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry)); @@ -1542,9 +1564,9 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key postCommandLocked(std::move(command)); return false; // wait for the command to run } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; } - } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + } else if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::SKIP) { if (*dropReason == DropReason::NOT_DROPPED) { *dropReason = DropReason::POLICY; } @@ -1560,9 +1582,10 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } // Identify targets. - std::vector<InputTarget> inputTargets; - InputEventInjectionResult injectionResult = - findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); + InputEventInjectionResult injectionResult; + sp<WindowInfoHandle> focusedWindow = + findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, + /*byref*/ injectionResult); if (injectionResult == InputEventInjectionResult::PENDING) { return false; } @@ -1571,6 +1594,12 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key if (injectionResult != InputEventInjectionResult::SUCCEEDED) { return true; } + LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr); + + std::vector<InputTarget> inputTargets; + addWindowTargetLocked(focusedWindow, + InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, + BitSet32(0), getDownTime(*entry), inputTargets); // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); @@ -1658,13 +1687,34 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< InputEventInjectionResult injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) - injectionResult = - findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, - &conflictingPointerActions); + + if (mDragState && + (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // If drag and drop ongoing and pointer down occur: pilfer drag window pointers + pilferPointersLocked(mDragState->dragWindow->getToken()); + } + + std::vector<TouchedWindow> touchedWindows = + findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions, + /*byref*/ injectionResult); + for (const TouchedWindow& touchedWindow : touchedWindows) { + LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED, + "Shouldn't be adding window if the injection didn't succeed."); + addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, + touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget, + inputTargets); + } } else { // Non touch event. (eg. trackball) - injectionResult = - findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); + sp<WindowInfoHandle> focusedWindow = + findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult); + if (injectionResult == InputEventInjectionResult::SUCCEEDED) { + LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr); + addWindowTargetLocked(focusedWindow, + InputTarget::Flags::FOREGROUND | + InputTarget::Flags::DISPATCH_AS_IS, + BitSet32(0), getDownTime(*entry), inputTargets); + } } if (injectionResult == InputEventInjectionResult::PENDING) { return false; @@ -1675,9 +1725,9 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< return true; } if (injectionResult != InputEventInjectionResult::SUCCEEDED) { - CancelationOptions::Mode mode(isPointerEvent - ? CancelationOptions::CANCEL_POINTER_EVENTS - : CancelationOptions::CANCEL_NON_POINTER_EVENTS); + CancelationOptions::Mode mode( + isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS + : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); synthesizeCancelationEventsForMonitorsLocked(options); return true; @@ -1688,7 +1738,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // Dispatch the motion. if (conflictingPointerActions) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } @@ -1714,22 +1764,23 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<Dr } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); } void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32 ", policyFlags=0x%x, " "action=%s, actionButton=0x%x, flags=0x%x, " "metaState=0x%x, buttonState=0x%x," "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, - entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(), - entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags, - entry.xPrecision, entry.yPrecision, entry.downTime); + prefix, entry.eventTime, entry.deviceId, + inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags, + MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags, + entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision, + entry.yPrecision, entry.downTime); for (uint32_t i = 0; i < entry.pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " @@ -1788,7 +1839,7 @@ void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) ALOGW("Canceling events for %s because it is unresponsive", connection->inputChannel->getName().c_str()); if (connection->status == Connection::Status::NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); } @@ -1866,10 +1917,11 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, return false; } -InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( - nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime) { +sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( + nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + InputEventInjectionResult& outInjectionResult) { std::string reason; + outInjectionResult = InputEventInjectionResult::FAILED; // Default result int32_t displayId = getTargetDisplayId(entry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); @@ -1882,12 +1934,12 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( ALOGI("Dropping %s event because there is no focused window or focused application in " "display %" PRId32 ".", ftl::enum_string(entry.type).c_str(), displayId); - return InputEventInjectionResult::FAILED; + return nullptr; } // Drop key events if requested by input feature if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) { - return InputEventInjectionResult::FAILED; + return nullptr; } // Compatibility behavior: raise ANR if there is a focused application, but no focused window. @@ -1907,15 +1959,17 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); *nextWakeupTime = *mNoFocusedWindowTimeoutTime; - return InputEventInjectionResult::PENDING; + outInjectionResult = InputEventInjectionResult::PENDING; + return nullptr; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", ftl::enum_string(entry.type).c_str()); - return InputEventInjectionResult::FAILED; + return nullptr; } else { // Still waiting for the focused window - return InputEventInjectionResult::PENDING; + outInjectionResult = InputEventInjectionResult::PENDING; + return nullptr; } } @@ -1925,13 +1979,15 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( // Verify targeted injection. if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) { ALOGW("Dropping injected event: %s", (*err).c_str()); - return InputEventInjectionResult::TARGET_MISMATCH; + outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH; + return nullptr; } if (focusedWindowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::PAUSE_DISPATCHING)) { ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); - return InputEventInjectionResult::PENDING; + outInjectionResult = InputEventInjectionResult::PENDING; + return nullptr; } // If the event is a key event, then we must wait for all previous events to @@ -1948,17 +2004,13 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; - return InputEventInjectionResult::PENDING; + outInjectionResult = InputEventInjectionResult::PENDING; + return nullptr; } } - // Success! Output targets. - addWindowTargetLocked(focusedWindowHandle, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, - BitSet32(0), inputTargets); - - // Done. - return InputEventInjectionResult::SUCCEEDED; + outInjectionResult = InputEventInjectionResult::SUCCEEDED; + return focusedWindowHandle; } /** @@ -1987,11 +2039,42 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( return responsiveMonitors; } -InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { +/** + * In general, touch should be always split between windows. Some exceptions: + * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's + * from the same device, *and* the window that's receiving the current pointer does not support + * split touch. + * 2. Don't split mouse events + */ +bool InputDispatcher::shouldSplitTouch(const TouchState& touchState, + const MotionEntry& entry) const { + if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) { + // We should never split mouse events + return false; + } + for (const TouchedWindow& touchedWindow : touchState.windows) { + if (touchedWindow.windowHandle->getInfo()->isSpy()) { + // Spy windows should not affect whether or not touch is split. + continue; + } + if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) { + continue; + } + // Eventually, touchedWindow will contain the deviceId of each pointer that's currently + // being sent there. For now, use deviceId from touch state. + if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) { + return false; + } + } + return true; +} + +std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( + nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, + InputEventInjectionResult& outInjectionResult) { ATRACE_CALL(); + std::vector<TouchedWindow> touchedWindows; // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. const int32_t displayId = entry.displayId; @@ -1999,7 +2082,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; // Update the touch state as needed based on the properties of the touch event. - InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; + outInjectionResult = InputEventInjectionResult::PENDING; sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); sp<WindowInfoHandle> newTouchedWindowHandle; @@ -2013,10 +2096,9 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState = *oldState; } - bool isSplit = tempTouchState.split; - bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 && - (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source || - tempTouchState.displayId != displayId); + bool isSplit = shouldSplitTouch(tempTouchState, entry); + const bool switchedDevice = (oldState != nullptr) && + (oldState->deviceId != entry.deviceId || oldState->source != entry.source); const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || @@ -2024,50 +2106,34 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); - bool wrongDevice = false; + if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (switchedDevice && tempTouchState.down && !down && !isHoverAction) { + if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) { ALOGI("Dropping event because a pointer for a different device is already down " "in display %" PRId32, displayId); // TODO: test multiple simultaneous input streams. - injectionResult = InputEventInjectionResult::FAILED; - switchedDevice = false; - wrongDevice = true; - goto Failed; + outInjectionResult = InputEventInjectionResult::FAILED; + return touchedWindows; // wrong device } tempTouchState.reset(); - tempTouchState.down = down; tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " "in display %" PRId32, displayId); // TODO: test multiple simultaneous input streams. - injectionResult = InputEventInjectionResult::FAILED; - switchedDevice = false; - wrongDevice = true; - goto Failed; + outInjectionResult = InputEventInjectionResult::FAILED; + return touchedWindows; // wrong device } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ - - int32_t x; - int32_t y; + const auto [x, y] = resolveTouchedPosition(entry); const int32_t pointerIndex = getMotionEventActionPointerIndex(action); - // Always dispatch mouse events to cursor position. - if (isFromMouse) { - x = int32_t(entry.xCursorPosition); - y = int32_t(entry.yCursorPosition); - } else { - x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); - y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); - } const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; const bool isStylus = isPointerFromStylus(entry, pointerIndex); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, @@ -2084,7 +2150,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected touch event: %s", (*err).c_str()); - injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; + outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; newTouchedWindowHandle = nullptr; goto Failed; } @@ -2103,7 +2169,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // No window is touched, so set split to true. This will allow the next pointer down to // be delivered to a new window which supports split touch. Pointers from a mouse device // should never be split. - tempTouchState.split = isSplit = !isFromMouse; + isSplit = !isFromMouse; } // Update hover state. @@ -2125,91 +2191,58 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( if (newTouchedWindows.empty()) { ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.", x, y, displayId); - injectionResult = InputEventInjectionResult::FAILED; + outInjectionResult = InputEventInjectionResult::FAILED; goto Failed; } for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { - const WindowInfo& info = *windowHandle->getInfo(); - - // Skip spy window targets that are not valid for targeted injection. - if (const auto err = verifyTargetedInjection(windowHandle, entry); err) { - continue; - } - - if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) { - ALOGI("Not sending touch event to %s because it is paused", - windowHandle->getName().c_str()); - continue; - } - - // Ensure the window has a connection and the connection is responsive - const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle); - if (!isResponsive) { - ALOGW("Not sending touch gesture to %s because it is not responsive", - windowHandle->getName().c_str()); - continue; - } - - // Drop events that can't be trusted due to occlusion - if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) { - TouchOcclusionInfo occlusionInfo = - computeTouchOcclusionInfoLocked(windowHandle, x, y); - if (!isTouchTrustedLocked(occlusionInfo)) { - if (DEBUG_TOUCH_OCCLUSION) { - ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y); - for (const auto& log : occlusionInfo.debugInfo) { - ALOGD("%s", log.c_str()); - } - } - sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage); - if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) { - ALOGW("Dropping untrusted touch event due to %s/%d", - occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid); - continue; - } - } - } - - // Drop touch events if requested by input feature - if (shouldDropInput(entry, windowHandle)) { + if (!canWindowReceiveMotionLocked(windowHandle, entry)) { continue; } // Set target flags. - int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS; + ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS; if (canReceiveForegroundTouches(*windowHandle->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. - targetFlags |= InputTarget::FLAG_FOREGROUND; + targetFlags |= InputTarget::Flags::FOREGROUND; } if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; + targetFlags |= InputTarget::Flags::SPLIT; } if (isWindowObscuredAtPointLocked(windowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(windowHandle)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } // Update the temporary touch state. BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[pointerIndex].id); - tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); + tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds, + entry.eventTime); } + + // If any existing window is pilfering pointers from newly added window, remove it + BitSet32 canceledPointers = BitSet32(0); + for (const TouchedWindow& window : tempTouchState.windows) { + if (window.isPilferingPointers) { + canceledPointers |= window.pointerIds; + } + } + tempTouchState.cancelPointersForNonPilferingWindows(canceledPointers); } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (!tempTouchState.down) { - if (DEBUG_FOCUS) { - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, - displayId); - } - injectionResult = InputEventInjectionResult::FAILED; + if (!tempTouchState.isDown()) { + ALOGD_IF(DEBUG_FOCUS, + "Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display %" PRId32 ": %s", + displayId, entry.getDescription().c_str()); + outInjectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -2218,9 +2251,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && tempTouchState.isSlippery()) { - const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - + const auto [x, y] = resolveTouchedPosition(entry); const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/); sp<WindowInfoHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); @@ -2230,7 +2261,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected event: %s", (*err).c_str()); - injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; + outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; newTouchedWindowHandle = nullptr; goto Failed; } @@ -2250,7 +2281,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } // Make a slippery exit from the old window. tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); // Make a slippery entrance into the new window. @@ -2258,22 +2289,24 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( isSplit = !isFromMouse; } - int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; + ftl::Flags<InputTarget::Flags> targetFlags = + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { - targetFlags |= InputTarget::FLAG_FOREGROUND; + targetFlags |= InputTarget::Flags::FOREGROUND; } if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; + targetFlags |= InputTarget::Flags::SPLIT; } if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[0].id); - tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds, + entry.eventTime); } } @@ -2304,7 +2337,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( mLastHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); + InputTarget::Flags::DISPATCH_AS_HOVER_EXIT, + BitSet32(0)); } // Let the new window know that the hover sequence is starting, unless we already did it @@ -2317,7 +2351,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(newHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, + InputTarget::Flags::DISPATCH_AS_HOVER_ENTER, BitSet32(0)); } } @@ -2330,11 +2364,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( [](const TouchedWindow& touchedWindow) { return !canReceiveForegroundTouches( *touchedWindow.windowHandle->getInfo()) || - (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; + touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND); })) { ALOGI("Dropping event because there is no touched window on display %d to receive it: %s", displayId, entry.getDescription().c_str()); - injectionResult = InputEventInjectionResult::FAILED; + outInjectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -2342,7 +2376,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( if (entry.injectionState != nullptr) { std::string errs; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { // Allow ACTION_OUTSIDE events generated by targeted injection to be // dispatched to any uid, since the coords will be zeroed out later. continue; @@ -2354,7 +2388,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( ALOGW("Dropping targeted injection: At least one touched window is not owned by uid " "%d:%s", *entry.injectionState->targetUid, errs.c_str()); - injectionResult = InputEventInjectionResult::TARGET_MISMATCH; + outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH; goto Failed; } } @@ -2367,11 +2401,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( if (foregroundWindowHandle) { const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle; if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) { tempTouchState.addOrUpdateWindow(windowInfoHandle, - InputTarget::FLAG_ZERO_COORDS, + InputTarget::Flags::ZERO_COORDS, BitSet32(0)); } } @@ -2400,25 +2434,20 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( WindowInfo::InputConfig::IS_WALLPAPER)) { BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[0].id); - tempTouchState - .addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED | - InputTarget:: - FLAG_WINDOW_IS_PARTIALLY_OBSCURED | - InputTarget::FLAG_DISPATCH_AS_IS, - pointerIds); + tempTouchState.addOrUpdateWindow(windowHandle, + InputTarget::Flags::WINDOW_IS_OBSCURED | + InputTarget::Flags:: + WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::Flags::DISPATCH_AS_IS, + pointerIds, entry.eventTime); } } } } // Success! Output targets. - injectionResult = InputEventInjectionResult::SUCCEEDED; - - for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, - touchedWindow.pointerIds, inputTargets); - } + touchedWindows = tempTouchState.windows; + outInjectionResult = InputEventInjectionResult::SUCCEEDED; // Drop the outside or hover touch windows since we will not care about them // in the next iteration. @@ -2426,73 +2455,70 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( Failed: // Update final pieces of touch state if the injector had permission. - if (!wrongDevice) { - if (switchedDevice) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Switched to a different device."); - } - *outConflictingPointerActions = true; + if (switchedDevice) { + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Switched to a different device."); } + *outConflictingPointerActions = true; + } - if (isHoverAction) { - // Started hovering, therefore no longer down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Hover received while pointer was " - "down."); - } - *outConflictingPointerActions = true; - } - tempTouchState.reset(); - if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || - maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - tempTouchState.deviceId = entry.deviceId; - tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP || - maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - tempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Down received while already down."); - } - *outConflictingPointerActions = true; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry.pointerProperties[pointerIndex].id; + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (oldState && oldState->isDown()) { + ALOGD_IF(DEBUG_FOCUS, + "Conflicting pointer actions: Hover received while pointer was down."); + *outConflictingPointerActions = true; + } + tempTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + tempTouchState.deviceId = entry.deviceId; + tempTouchState.source = entry.source; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + // All pointers up or canceled. + tempTouchState.reset(); + } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + // First pointer went down. + if (oldState && oldState->isDown()) { + ALOGD("Conflicting pointer actions: Down received while already down."); + *outConflictingPointerActions = true; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + // One pointer went up. + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - for (size_t i = 0; i < tempTouchState.windows.size();) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - tempTouchState.windows.erase(tempTouchState.windows.begin() + i); - continue; - } - i += 1; + for (size_t i = 0; i < tempTouchState.windows.size();) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + tempTouchState.windows.erase(tempTouchState.windows.begin() + i); + continue; } + i += 1; } + } - // Save changes unless the action was scroll in which case the temporary touch - // state was only valid for this one action. - if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (tempTouchState.displayId >= 0) { - mTouchStatesByDisplay[displayId] = tempTouchState; - } else { - mTouchStatesByDisplay.erase(displayId); - } + // Save changes unless the action was scroll in which case the temporary touch + // state was only valid for this one action. + if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { + if (displayId >= 0) { + mTouchStatesByDisplay[displayId] = tempTouchState; + } else { + mTouchStatesByDisplay.erase(displayId); } + } - // Update hover state. - mLastHoverWindowHandle = newHoverWindowHandle; + if (tempTouchState.windows.empty()) { + mTouchStatesByDisplay.erase(displayId); } - return injectionResult; + // Update hover state. + mLastHoverWindowHandle = newHoverWindowHandle; + + return touchedWindows; } void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { @@ -2597,8 +2623,10 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { } void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, - std::vector<InputTarget>& inputTargets) { + ftl::Flags<InputTarget::Flags> targetFlags, + BitSet32 pointerIds, + std::optional<nsecs_t> firstDownTimeInTarget, + std::vector<InputTarget>& inputTargets) const { std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { @@ -2619,6 +2647,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTarget.firstDownTimeInTarget = firstDownTimeInTarget; const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId); if (displayInfoIt != mDisplayInfos.end()) { inputTarget.displayTransform = displayInfoIt->second.transform; @@ -2643,7 +2672,9 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { InputTarget target; target.inputChannel = monitor.inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; + // target.firstDownTimeInTarget is not set for global monitors. It is only required in split + // touch and global monitoring works as intended even without setting firstDownTimeInTarget if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { target.displayTransform = it->second.transform; } @@ -2673,7 +2704,7 @@ static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, // We do want to potentially flag touchable windows even if they have 0 // opacity, since they can consume touches and alter the effects of the // user interaction (eg. apps that rely on - // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those + // Flags::WINDOW_IS_PARTIALLY_OBSCURED should still be told about those // windows), hence we also check for FLAG_NOT_TOUCHABLE. return false; } else if (info->ownerUid == otherInfo->ownerUid) { @@ -2902,9 +2933,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ATRACE_NAME(message.c_str()); } if (DEBUG_DISPATCH_CYCLE) { - ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " + ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, " "globalScaleFactor=%f, pointerIds=0x%x %s", - connection->getInputChannelName().c_str(), inputTarget.flags, + connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(), inputTarget.globalScaleFactor, inputTarget.pointerIds.value, inputTarget.getPointerInfoString().c_str()); } @@ -2921,15 +2952,19 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (inputTarget.flags & InputTarget::FLAG_SPLIT) { + if (inputTarget.flags.test(InputTarget::Flags::SPLIT)) { LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, - "Entry type %s should not have FLAG_SPLIT", + "Entry type %s should not have Flags::SPLIT", ftl::enum_string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { + LOG_ALWAYS_FATAL_IF(!inputTarget.firstDownTimeInTarget.has_value(), + "Splitting motion events requires a down time to be set for the " + "target"); std::unique_ptr<MotionEntry> splitMotionEntry = - splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); + splitMotionEvent(originalMotionEntry, inputTarget.pointerIds, + inputTarget.firstDownTimeInTarget.value()); if (!splitMotionEntry) { return; // split event was dropped } @@ -2963,22 +2998,24 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } + LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK), + "No dispatch flags are set for %s", eventEntry->getDescription().c_str()); - bool wasEmpty = connection->outboundQueue.empty(); + const bool wasEmpty = connection->outboundQueue.empty(); // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + InputTarget::Flags::DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + InputTarget::Flags::DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + InputTarget::Flags::DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); + InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.empty()) { @@ -2989,18 +3026,20 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget, - int32_t dispatchMode) { + ftl::Flags<InputTarget::Flags> dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", connection->getInputChannelName().c_str(), - dispatchModeToString(dispatchMode).c_str()); + dispatchMode.string().c_str()); ATRACE_NAME(message.c_str()); } - int32_t inputTargetFlags = inputTarget.flags; - if (!(inputTargetFlags & dispatchMode)) { + ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags; + if (!inputTargetFlags.any(dispatchMode)) { return; } - inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; + + inputTargetFlags.clear(InputTarget::DISPATCH_MASK); + inputTargetFlags |= dispatchMode; // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. @@ -3037,15 +3076,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio constexpr int32_t DEFAULT_RESOLVED_EVENT_ID = static_cast<int32_t>(IdGenerator::Source::OTHER); dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID; - if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; } else { dispatchEntry->resolvedAction = motionEntry.action; @@ -3065,10 +3104,10 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } dispatchEntry->resolvedFlags = motionEntry.flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) { dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { + if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) { dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } @@ -3168,8 +3207,7 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens; std::vector<sp<Connection>> newConnections; for (const InputTarget& target : targets) { - if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == - InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { continue; // Skip windows that receive ACTION_OUTSIDE } @@ -3218,6 +3256,55 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a postCommandLocked(std::move(command)); } +status_t InputDispatcher::publishMotionEvent(Connection& connection, + DispatchEntry& dispatchEntry) const { + const EventEntry& eventEntry = *(dispatchEntry.eventEntry); + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); + + PointerCoords scaledCoords[MAX_POINTERS]; + const PointerCoords* usingCoords = motionEntry.pointerCoords; + + // Set the X and Y offset and X and Y scale depending on the input source. + if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) && + !(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) { + float globalScaleFactor = dispatchEntry.globalScaleFactor; + if (globalScaleFactor != 1.0f) { + for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { + scaledCoords[i] = motionEntry.pointerCoords[i]; + // Don't apply window scale here since we don't want scale to affect raw + // coordinates. The scale will be sent back to the client and applied + // later when requesting relative coordinates. + scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */, + 1 /* windowYScale */); + } + usingCoords = scaledCoords; + } + } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) { + // We don't want the dispatch target to know the coordinates + for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { + scaledCoords[i].clear(); + } + usingCoords = scaledCoords; + } + + std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry); + + // Publish the motion event. + return connection.inputPublisher + .publishMotionEvent(dispatchEntry.seq, dispatchEntry.resolvedEventId, + motionEntry.deviceId, motionEntry.source, motionEntry.displayId, + std::move(hmac), dispatchEntry.resolvedAction, + motionEntry.actionButton, dispatchEntry.resolvedFlags, + motionEntry.edgeFlags, motionEntry.metaState, + motionEntry.buttonState, motionEntry.classification, + dispatchEntry.transform, motionEntry.xPrecision, + motionEntry.yPrecision, motionEntry.xCursorPosition, + motionEntry.yCursorPosition, dispatchEntry.rawTransform, + motionEntry.downTime, motionEntry.eventTime, + motionEntry.pointerCount, motionEntry.pointerProperties, + usingCoords); +} + void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { if (ATRACE_ENABLED()) { @@ -3257,58 +3344,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); - - PointerCoords scaledCoords[MAX_POINTERS]; - const PointerCoords* usingCoords = motionEntry.pointerCoords; - - // Set the X and Y offset and X and Y scale depending on the input source. - if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) && - !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { - float globalScaleFactor = dispatchEntry->globalScaleFactor; - if (globalScaleFactor != 1.0f) { - for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { - scaledCoords[i] = motionEntry.pointerCoords[i]; - // Don't apply window scale here since we don't want scale to affect raw - // coordinates. The scale will be sent back to the client and applied - // later when requesting relative coordinates. - scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */, - 1 /* windowYScale */); - } - usingCoords = scaledCoords; - } - } else { - // We don't want the dispatch target to know. - if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { - scaledCoords[i].clear(); - } - usingCoords = scaledCoords; - } - } - - std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry); - - // Publish the motion event. - status = connection->inputPublisher - .publishMotionEvent(dispatchEntry->seq, - dispatchEntry->resolvedEventId, - motionEntry.deviceId, motionEntry.source, - motionEntry.displayId, std::move(hmac), - dispatchEntry->resolvedAction, - motionEntry.actionButton, - dispatchEntry->resolvedFlags, - motionEntry.edgeFlags, motionEntry.metaState, - motionEntry.buttonState, - motionEntry.classification, - dispatchEntry->transform, - motionEntry.xPrecision, motionEntry.yPrecision, - motionEntry.xCursorPosition, - motionEntry.yCursorPosition, - dispatchEntry->rawTransform, - motionEntry.downTime, motionEntry.eventTime, - motionEntry.pointerCount, - motionEntry.pointerProperties, usingCoords); + status = publishMotionEvent(*connection, *dispatchEntry); break; } @@ -3645,7 +3681,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; const bool wasEmpty = connection->outboundQueue.empty(); @@ -3680,7 +3716,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); } // If the outbound queue was previously empty, start the dispatch cycle going. @@ -3690,15 +3726,13 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( - const sp<Connection>& connection) { + const nsecs_t downTime, const sp<Connection>& connection) { if (connection->status == Connection::Status::BROKEN) { return; } - nsecs_t currentTime = now(); - std::vector<std::unique_ptr<EventEntry>> downEvents = - connection->inputState.synthesizePointerDownEvents(currentTime); + connection->inputState.synthesizePointerDownEvents(downTime); if (downEvents.empty()) { return; @@ -3718,10 +3752,9 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; const bool wasEmpty = connection->outboundQueue.empty(); - for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { switch (downEventEntry->type) { case EventEntry::Type::MOTION: { @@ -3745,16 +3778,17 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); } + // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.empty()) { - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(downTime, connection); } } std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( - const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { + const MotionEntry& originalMotionEntry, BitSet32 pointerIds, nsecs_t splitDownTime) { ALOG_ASSERT(pointerIds.value != 0); uint32_t splitPointerIndexMap[MAX_POINTERS]; @@ -3822,6 +3856,13 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( } } + if (action == AMOTION_EVENT_ACTION_DOWN) { + LOG_ALWAYS_FATAL_IF(splitDownTime != originalMotionEntry.eventTime, + "Split motion event has mismatching downTime and eventTime for " + "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64 "ms", + originalMotionEntry.getDescription().c_str(), ns2ms(splitDownTime)); + } + int32_t newId = mIdGenerator.nextId(); if (ATRACE_ENABLED()) { std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32 @@ -3842,9 +3883,9 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.xPrecision, originalMotionEntry.yPrecision, originalMotionEntry.xCursorPosition, - originalMotionEntry.yCursorPosition, - originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords); + originalMotionEntry.yCursorPosition, splitDownTime, + splitPointerCount, splitPointerProperties, + splitPointerCoords); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3992,13 +4033,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { if (DEBUG_INBOUND_EVENT_DETAILS) { ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " "yCursorPosition=%f, downTime=%" PRId64, args->id, args->eventTime, args->deviceId, args->source, args->displayId, - args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, - args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, - args->xCursorPosition, args->yCursorPosition, args->downTime); + args->policyFlags, MotionEvent::actionToString(args->action).c_str(), + args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, + args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -4016,10 +4058,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } } - if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, - args->pointerProperties)) { - return; - } + LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount, + args->pointerProperties), + "Invalid event: %s", args->dump().c_str()); uint32_t policyFlags = args->policyFlags; policyFlags |= POLICY_FLAG_TRUSTED; @@ -4582,26 +4623,58 @@ sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId return getWindowHandleLocked(focusedToken, displayId); } -bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const { - sp<Connection> connection = getConnectionLocked(windowHandle.getToken()); - const bool noInputChannel = - windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); - if (connection != nullptr && noInputChannel) { - ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s", - windowHandle.getName().c_str(), connection->inputChannel->getName().c_str()); +bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window, + const MotionEntry& motionEntry) const { + const WindowInfo& info = *window->getInfo(); + + // Skip spy window targets that are not valid for targeted injection. + if (const auto err = verifyTargetedInjection(window, motionEntry); err) { return false; } + if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) { + ALOGI("Not sending touch event to %s because it is paused", window->getName().c_str()); + return false; + } + + if (info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { + ALOGW("Not sending touch gesture to %s because it has config NO_INPUT_CHANNEL", + window->getName().c_str()); + return false; + } + + sp<Connection> connection = getConnectionLocked(window->getToken()); if (connection == nullptr) { - if (!noInputChannel) { - ALOGI("Could not find connection for %s", windowHandle.getName().c_str()); - } + ALOGW("Not sending touch to %s because there's no corresponding connection", + window->getName().c_str()); return false; } + if (!connection->responsive) { - ALOGW("Window %s is not responsive", windowHandle.getName().c_str()); + ALOGW("Not sending touch to %s because it is not responsive", window->getName().c_str()); return false; } + + // Drop events that can't be trusted due to occlusion + const auto [x, y] = resolveTouchedPosition(motionEntry); + TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y); + if (!isTouchTrustedLocked(occlusionInfo)) { + if (DEBUG_TOUCH_OCCLUSION) { + ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y); + for (const auto& log : occlusionInfo.debugInfo) { + ALOGD("%s", log.c_str()); + } + } + ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(), + occlusionInfo.obscuringUid); + return false; + } + + // Drop touch events if requested by input feature + if (shouldDropInput(motionEntry, window)) { + return false; + } + return true; } @@ -4765,12 +4838,12 @@ void InputDispatcher::setInputWindowsLocked( std::shared_ptr<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND && + if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); @@ -4811,7 +4884,7 @@ void InputDispatcher::setInputWindowsLocked( std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(newWindowHandle->getToken()); if (inputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window's orientation changed"); synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); } @@ -4892,7 +4965,7 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { getInputChannelLocked(oldFocusedWindowToken); if (inputChannel != nullptr) { CancelationOptions - options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, "The display which contains this window no longer has focus."); options.displayId = ADISPLAY_ID_NONE; synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); @@ -4912,10 +4985,6 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } } } - - if (DEBUG_FOCUS) { - logDispatchStateLocked(); - } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -4946,10 +5015,6 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } else { changed = false; } - - if (DEBUG_FOCUS) { - logDispatchStateLocked(); - } } // release lock if (changed) { @@ -4978,19 +5043,23 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { mLooper->wake(); } -bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, - bool hasPermission) { +bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission, + int32_t displayId) { bool needWake = false; { std::scoped_lock lock(mLock); - if (mInTouchMode == inTouchMode) { + ALOGD_IF(DEBUG_TOUCH_MODE, + "Request to change touch mode to %s (calling pid=%d, uid=%d, " + "hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)", + toString(inTouchMode), pid, uid, toString(hasPermission), displayId, + mTouchModePerDisplay.count(displayId) == 0 + ? "not set" + : std::to_string(mTouchModePerDisplay[displayId]).c_str()); + + auto touchModeIt = mTouchModePerDisplay.find(displayId); + if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) { return false; } - if (DEBUG_TOUCH_MODE) { - ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, " - "hasPermission=%s)", - toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission)); - } if (!hasPermission) { if (!focusedWindowIsOwnedByLocked(pid, uid) && !recentWindowsAreOwnedByLocked(pid, uid)) { @@ -5000,11 +5069,9 @@ bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, return false; } } - - // TODO(b/198499018): Store touch mode per display. - mInTouchMode = inTouchMode; - - auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode); + mTouchModePerDisplay[displayId] = inTouchMode; + auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode, + displayId); needWake = enqueueInboundEventLocked(std::move(entry)); } // release lock @@ -5042,21 +5109,16 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { mMaximumObscuringOpacityForTouch = opacity; } -void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) { - std::scoped_lock lock(mLock); - mBlockUntrustedTouchesMode = mode; -} - -std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked( - const sp<IBinder>& token) { +std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> +InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) { for (auto& [displayId, state] : mTouchStatesByDisplay) { for (TouchedWindow& w : state.windows) { if (w.windowHandle->getToken() == token) { - return std::make_pair(&state, &w); + return std::make_tuple(&state, &w, displayId); } } } - return std::make_pair(nullptr, nullptr); + return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT); } bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, @@ -5072,13 +5134,12 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< std::scoped_lock _l(mLock); // Find the target touch state and touched window by fromToken. - auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken); + auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken); if (state == nullptr || touchedWindow == nullptr) { ALOGD("Focus transfer failed because from window is not being touched."); return false; } - const int32_t displayId = state->displayId; sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (toWindowHandle == nullptr) { ALOGW("Cannot transfer focus because to window not found."); @@ -5092,17 +5153,18 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } // Erase old window. - int32_t oldTargetFlags = touchedWindow->targetFlags; + ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; BitSet32 pointerIds = touchedWindow->pointerIds; state->removeWindowByToken(fromToken); // Add new window. - int32_t newTargetFlags = - oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); + nsecs_t downTimeInTarget = now(); + ftl::Flags<InputTarget::Flags> newTargetFlags = + oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS); if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) { - newTargetFlags |= InputTarget::FLAG_FOREGROUND; + newTargetFlags |= InputTarget::Flags::FOREGROUND; } - state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); + state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget); // Store the dragging window. if (isDragDrop) { @@ -5122,14 +5184,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions - options(CancelationOptions::CANCEL_POINTER_EVENTS, + options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); - synthesizePointerDownEventsForConnectionLocked(toConnection); - } - - if (DEBUG_FOCUS) { - logDispatchStateLocked(); + synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection); } } // release lock @@ -5154,7 +5212,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(int32_t sp<WindowInfoHandle> touchedForegroundWindow; // If multiple foreground windows are touched, return nullptr for (const TouchedWindow& window : state.windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { if (touchedForegroundWindow != nullptr) { ALOGI("Two or more foreground windows: %s and %s", touchedForegroundWindow->getName().c_str(), @@ -5196,7 +5254,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { ALOGD("Resetting and dropping all events (%s).", reason); } - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); resetKeyRepeatLocked(); @@ -5266,23 +5324,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); - for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) { - const TouchState& state = pair.second; - dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", - state.displayId, toString(state.down), toString(state.split), - state.deviceId, state.source); - if (!state.windows.empty()) { - dump += INDENT3 "Windows:\n"; - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - dump += StringPrintf(INDENT4 - "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.windowHandle->getName().c_str(), - touchedWindow.pointerIds.value, touchedWindow.targetFlags); - } - } else { - dump += INDENT3 "Windows: <none>\n"; - } + for (const auto& [displayId, state] : mTouchStatesByDisplay) { + std::string touchStateDump = addLinePrefix(state.dump(), INDENT2); + dump += INDENT2 + std::to_string(displayId) + " : " + touchStateDump; } } else { dump += INDENT "TouchStates: <no displays touched>\n"; @@ -5391,9 +5435,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mReplacedKeys.empty()) { dump += INDENT "ReplacedKeys:\n"; - for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) { - const KeyReplacement& replacement = pair.first; - int32_t newKeyCode = pair.second; + for (const auto& [replacement, newKeyCode] : mReplacedKeys) { dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n", replacement.keyCode, replacement.deviceId, newKeyCode); } @@ -5446,6 +5488,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "AppSwitch: not pending\n"; } + if (!mTouchModePerDisplay.empty()) { + dump += INDENT "TouchModePerDisplay:\n"; + for (const auto& [displayId, touchMode] : mTouchModePerDisplay) { + dump += StringPrintf(INDENT2 "Display: %" PRId32 " TouchMode: %s\n", displayId, + std::to_string(touchMode).c_str()); + } + } else { + dump += INDENT "TouchModePerDisplay: <none>\n"; + } + dump += INDENT "Configuration:\n"; dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", @@ -5491,7 +5543,7 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const const sp<IBinder>& token = serverChannel->getConnectionToken(); int fd = serverChannel->getFd(); sp<Connection> connection = - new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator); + sp<Connection>::make(std::move(serverChannel), false /*monitor*/, mIdGenerator); if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { ALOGE("Created a new connection, but the token %p is already known", token.get()); @@ -5501,7 +5553,8 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + nullptr); } // release lock // Wake the looper because some connections have changed. @@ -5527,7 +5580,8 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ << " without a specified display."; } - sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator); + sp<Connection> connection = + sp<Connection>::make(serverChannel, true /*monitor*/, mIdGenerator); const sp<IBinder>& token = serverChannel->getConnectionToken(); const int fd = serverChannel->getFd(); @@ -5540,7 +5594,8 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + nullptr); } // Wake the looper because some connections have changed. @@ -5604,31 +5659,35 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionTo status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { std::scoped_lock _l(mLock); + return pilferPointersLocked(token); +} +status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token); if (!requestingChannel) { ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token"); return BAD_VALUE; } - auto [statePtr, windowPtr] = findTouchStateAndWindowLocked(token); - if (statePtr == nullptr || windowPtr == nullptr || !statePtr->down) { + auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token); + if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.isEmpty()) { ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams." " Ignoring."); return BAD_VALUE; } TouchState& state = *statePtr; - + TouchedWindow& window = *windowPtr; // Send cancel events to all the input channels we're stealing from. - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "input channel stole pointer stream"); options.deviceId = state.deviceId; - options.displayId = state.displayId; + options.displayId = displayId; + options.pointerIds = window.pointerIds; std::string canceledWindows; - for (const TouchedWindow& window : state.windows) { + for (const TouchedWindow& w : state.windows) { const std::shared_ptr<InputChannel> channel = - getInputChannelLocked(window.windowHandle->getToken()); + getInputChannelLocked(w.windowHandle->getToken()); if (channel != nullptr && channel->getConnectionToken() != token) { synthesizeCancelationEventsForInputChannelLocked(channel, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; @@ -5640,8 +5699,10 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { canceledWindows.c_str()); // Prevent the gesture from being sent to any other windows. - state.filterWindowsExcept(token); - state.preventNewTargets = true; + // This only blocks relevant pointers to be sent to other windows + window.isPilferingPointers = true; + + state.cancelPointersForWindowsExcept(window.pointerIds, token); return OK; } @@ -5813,14 +5874,6 @@ void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, floa postCommandLocked(std::move(command)); } -void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) { - auto command = [this, obscuringPackage]() REQUIRES(mLock) { - scoped_unlock unlock(mLock); - mPolicy->notifyUntrustedTouch(obscuringPackage); - }; - postCommandLocked(std::move(command)); -} - void InputDispatcher::onAnrLocked(const sp<Connection>& connection) { if (connection == nullptr) { LOG_ALWAYS_FATAL("Caller must check for nullness"); @@ -5912,11 +5965,11 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& } // acquire lock if (delay < 0) { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP; } else if (delay == 0) { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; } else { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER; entry.interceptKeyWakeupTime = now() + delay; } } @@ -6026,7 +6079,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con // Cancel the fallback key. if (fallbackKeyCode != AKEYCODE_UNKNOWN) { - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "application handled the original non-fallback key " "or is no longer a foreground target, " "canceling previously dispatched fallback key"); @@ -6103,7 +6156,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con } } - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "canceling fallback, policy no longer desires it"); options.keyCode = fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); @@ -6256,7 +6309,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch if (changes.oldFocus) { std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus); if (focusedInputChannel) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason); @@ -6330,6 +6383,8 @@ void InputDispatcher::displayRemoved(int32_t displayId) { mFocusResolver.displayRemoved(displayId); // Reset pointer capture eligibility, regardless of previous state. std::erase(mIneligibleDisplaysForPointerCapture, displayId); + // Remove the associated touch mode state. + mTouchModePerDisplay.erase(displayId); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -6343,7 +6398,7 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& window std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay; for (const auto& info : windowInfos) { handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>()); - handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info)); + handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info)); } { // acquire lock @@ -6394,7 +6449,7 @@ void InputDispatcher::cancelCurrentTouch() { { std::scoped_lock _l(mLock); ALOGD("Canceling all ongoing pointer gestures on all displays."); - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "cancel current touch"); synthesizeCancelationEventsForAllConnectionsLocked(options); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index ed89ed0b0f..5efb39e0f2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_DISPATCHER_H -#define _UI_INPUT_DISPATCHER_H +#pragma once #include "AnrTracker.h" #include "CancelationOptions.h" @@ -42,7 +41,6 @@ #include <input/InputTransport.h> #include <limits.h> #include <stddef.h> -#include <ui/Region.h> #include <unistd.h> #include <utils/BitSet.h> #include <utils/Looper.h> @@ -119,9 +117,9 @@ public: void setFocusedDisplay(int32_t displayId) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; - bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) override; + bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission, + int32_t displayId) override; void setMaximumObscuringOpacityForTouch(float opacity) override; - void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop = false) override; @@ -241,7 +239,7 @@ private: sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked( int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false, - bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock); + bool addOutsideTargets = false, bool ignoreDragWindow = false) const REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked( int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock); @@ -256,6 +254,8 @@ private: void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock); + status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock); + template <typename T> struct StrongPointerHash { std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); } @@ -342,9 +342,12 @@ private: bool mDispatchEnabled GUARDED_BY(mLock); bool mDispatchFrozen GUARDED_BY(mLock); bool mInputFilterEnabled GUARDED_BY(mLock); - bool mInTouchMode GUARDED_BY(mLock); float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); - android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); + + // This map is not really needed, but it helps a lot with debugging (dumpsys input). + // In the java layer, touch mode states are spread across multiple DisplayContent objects, + // making harder to snapshot and retrieve them. + std::map<int32_t /*displayId*/, bool /*inTouchMode*/> mTouchModePerDisplay GUARDED_BY(mLock); class DispatcherWindowListener : public gui::WindowInfosListener { public: @@ -381,10 +384,10 @@ private: REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); - bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const - REQUIRES(mLock); + bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window, + const MotionEntry& motionEntry) const REQUIRES(mLock); - // Gets all the input targets (with their respective input channels) from the window handles + // Returns all the input targets (with their respective input channels) from the window handles // passed as argument. std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked( const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const @@ -538,19 +541,21 @@ private: // shade is pulled down while we are counting down the timeout). void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); + bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const; int32_t getTargetDisplayId(const EventEntry& entry); - android::os::InputEventInjectionResult findFocusedWindowTargetsLocked( - nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime) REQUIRES(mLock); - android::os::InputEventInjectionResult findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); + sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( + nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); + std::vector<TouchedWindow> findTouchedWindowTargetsLocked( + nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, + android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<Monitor> selectResponsiveMonitorsLocked( const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, - std::vector<InputTarget>& inputTargets) REQUIRES(mLock); + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, + std::optional<nsecs_t> firstDownTimeInTarget, + std::vector<InputTarget>& inputTargets) const REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId) REQUIRES(mLock); void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); @@ -595,8 +600,9 @@ private: std::shared_ptr<EventEntry>, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>, - const InputTarget& inputTarget, int32_t dispatchMode) - REQUIRES(mLock); + const InputTarget& inputTarget, + ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock); + status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const; void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, @@ -621,12 +627,14 @@ private: const CancelationOptions& options) REQUIRES(mLock); - void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection) + void synthesizePointerDownEventsForConnectionLocked(const nsecs_t downTime, + const sp<Connection>& connection) REQUIRES(mLock); - // Splitting motion events across windows. + // Splitting motion events across windows. When splitting motion event for a target, + // splitDownTime refers to the time of first 'down' event on that particular target std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, - BitSet32 pointerIds); + BitSet32 pointerIds, nsecs_t splitDownTime); // Reset and drop everything the dispatcher is doing. void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); @@ -652,7 +660,6 @@ private: void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) REQUIRES(mLock); void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock); - void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock); void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock); void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window, @@ -669,8 +676,8 @@ private: bool handled) REQUIRES(mLock); // Find touched state and touched window by token. - std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) - REQUIRES(mLock); + std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> + findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock); // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); @@ -687,5 +694,3 @@ private: }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_DISPATCHER_H diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h index 77b8472f89..daf375d918 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.h +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H +#pragma once #include <binder/IBinder.h> #include <input/Input.h> @@ -104,5 +103,3 @@ public: } // namespace inputdispatcher } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index f46a8bce7e..563868d10e 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -286,19 +286,30 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents for (const MotionMemento& memento : mMotionMementos) { if (shouldCancelMotion(memento, options)) { - const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT - : AMOTION_EVENT_ACTION_CANCEL; - events.push_back( - std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, - memento.deviceId, memento.source, - memento.displayId, memento.policyFlags, action, - 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, - memento.pointerCount, memento.pointerProperties, - memento.pointerCoords)); + if (options.pointerIds == std::nullopt) { + const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT + : AMOTION_EVENT_ACTION_CANCEL; + events.push_back( + std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, + memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, + action, 0 /*actionButton*/, memento.flags, + AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + memento.xPrecision, memento.yPrecision, + memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + memento.pointerCount, + memento.pointerProperties, + memento.pointerCoords)); + } else { + std::vector<std::unique_ptr<MotionEntry>> pointerCancelEvents = + synthesizeCancelationEventsForPointers(memento, options.pointerIds.value(), + currentTime); + events.insert(events.end(), std::make_move_iterator(pointerCancelEvents.begin()), + std::make_move_iterator(pointerCancelEvents.end())); + } } } return events; @@ -359,6 +370,73 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents return events; } +std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEventsForPointers( + const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime) { + std::vector<std::unique_ptr<MotionEntry>> events; + std::vector<uint32_t> canceledPointerIndices; + std::vector<PointerProperties> pointerProperties(MAX_POINTERS); + std::vector<PointerCoords> pointerCoords(MAX_POINTERS); + for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) { + uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id); + pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]); + pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]); + if (pointerIds.hasBit(pointerId)) { + canceledPointerIndices.push_back(pointerIdx); + } + } + + if (canceledPointerIndices.size() == memento.pointerCount) { + const int32_t action = + memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; + events.push_back( + std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId, + memento.source, memento.displayId, + memento.policyFlags, action, 0 /*actionButton*/, + memento.flags, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + memento.pointerCount, memento.pointerProperties, + memento.pointerCoords)); + } else { + // If we aren't canceling all pointers, we need to generated ACTION_POINTER_UP with + // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the + // previously canceled pointers from PointerProperties and PointerCoords, and update + // pointerCount appropriately. For convenience, sort the canceled pointer indices so that we + // can just slide the remaining pointers to the beginning of the array when a pointer is + // canceled. + std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(), + std::greater<uint32_t>()); + + uint32_t pointerCount = memento.pointerCount; + for (const uint32_t pointerIdx : canceledPointerIndices) { + const int32_t action = pointerCount == 1 ? AMOTION_EVENT_ACTION_CANCEL + : AMOTION_EVENT_ACTION_POINTER_UP | + (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + events.push_back( + std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, + memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, + memento.flags | AMOTION_EVENT_FLAG_CANCELED, + AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + pointerCount, pointerProperties.data(), + pointerCoords.data())); + + // Cleanup pointer information + pointerProperties.erase(pointerProperties.begin() + pointerIdx); + pointerCoords.erase(pointerCoords.begin() + pointerIdx); + pointerCount--; + } + } + return events; +} + void InputState::clear() { mKeyMementos.clear(); mMotionMementos.clear(); @@ -422,10 +500,10 @@ bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOpt } switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return true; - case CancelationOptions::CANCEL_FALLBACK_EVENTS: + case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: return memento.flags & AKEY_EVENT_FLAG_FALLBACK; default: return false; @@ -443,11 +521,11 @@ bool InputState::shouldCancelMotion(const MotionMemento& memento, } switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: return true; - case CancelationOptions::CANCEL_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: return memento.source & AINPUT_SOURCE_CLASS_POINTER; - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); default: return false; diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 74ae21f76a..6ab48c9273 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H +#pragma once #include "CancelationOptions.h" #include "Entry.h" #include <utils/Timers.h> -namespace android::inputdispatcher { +namespace android { +namespace inputdispatcher { static constexpr int32_t INVALID_POINTER_INDEX = -1; @@ -125,8 +125,11 @@ private: static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options); -}; -} // namespace android::inputdispatcher + // Synthesizes pointer cancel events for a particular set of pointers. + std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers( + const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime); +}; -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H +} // namespace inputdispatcher +} // namespace android diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 2df97d9a12..2f39480555 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -24,24 +24,6 @@ using android::base::StringPrintf; namespace android::inputdispatcher { -std::string dispatchModeToString(int32_t dispatchMode) { - switch (dispatchMode) { - case InputTarget::FLAG_DISPATCH_AS_IS: - return "DISPATCH_AS_IS"; - case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: - return "DISPATCH_AS_OUTSIDE"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: - return "DISPATCH_AS_HOVER_ENTER"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: - return "DISPATCH_AS_HOVER_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: - return "DISPATCH_AS_SLIPPERY_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: - return "DISPATCH_AS_SLIPPERY_ENTER"; - } - return StringPrintf("%" PRId32, dispatchMode); -} - void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) { // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no // valid pointer property from the input event. diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 0725389ed6..61b07feb3a 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -14,9 +14,9 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H +#pragma once +#include <ftl/flags.h> #include <gui/constants.h> #include <input/InputTransport.h> #include <ui/Transform.h> @@ -31,70 +31,70 @@ namespace android::inputdispatcher { * window area. */ struct InputTarget { - enum { + enum class Flags : uint32_t { /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 1 << 0, + FOREGROUND = 1 << 0, /* This flag indicates that the MotionEvent falls within the area of the target * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 1 << 1, + WINDOW_IS_OBSCURED = 1 << 1, /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 1 << 2, + SPLIT = 1 << 2, /* This flag indicates that the pointer coordinates dispatched to the application * will be zeroed out to avoid revealing information to an application. This is * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing * the same UID from watching all touches. */ - FLAG_ZERO_COORDS = 1 << 3, + ZERO_COORDS = 1 << 3, /* This flag indicates that the event should be sent as is. * Should always be set unless the event is to be transmuted. */ - FLAG_DISPATCH_AS_IS = 1 << 8, + DISPATCH_AS_IS = 1 << 8, /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside * of the area of this target and so should instead be delivered as an * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + DISPATCH_AS_OUTSIDE = 1 << 9, /* This flag indicates that a hover sequence is starting in the given window. * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + DISPATCH_AS_HOVER_ENTER = 1 << 10, /* This flag indicates that a hover event happened outside of a window which handled * previous hover events, signifying the end of the current hover sequence for that * window. * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + DISPATCH_AS_HOVER_EXIT = 1 << 11, /* This flag indicates that the event should be canceled. * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips * outside of a window. */ - FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, + DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, /* This flag indicates that the event should be dispatched as an initial down. * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips * into a new window. */ - FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, - - /* Mask for all dispatch modes. */ - FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | - FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | - FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, + DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, /* This flag indicates that the target of a MotionEvent is partly or wholly * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - + WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, }; + /* Mask for all dispatch modes. */ + static constexpr const ftl::Flags<InputTarget::Flags> DISPATCH_MASK = + ftl::Flags<InputTarget::Flags>() | Flags::DISPATCH_AS_IS | Flags::DISPATCH_AS_OUTSIDE | + Flags::DISPATCH_AS_HOVER_ENTER | Flags::DISPATCH_AS_HOVER_EXIT | + Flags::DISPATCH_AS_SLIPPERY_EXIT | Flags::DISPATCH_AS_SLIPPERY_ENTER; + // The input channel to be targeted. std::shared_ptr<InputChannel> inputChannel; // Flags for the input target. - int32_t flags = 0; + ftl::Flags<Flags> flags; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) @@ -106,6 +106,9 @@ struct InputTarget { // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; + // Event time for the first motion event (ACTION_DOWN) dispatched to this input target if + // FLAG_SPLIT is set. + std::optional<nsecs_t> firstDownTimeInTarget; // The data is stored by the pointerId. Use the bit position of pointerIds to look up // Transform per pointerId. ui::Transform pointerTransforms[MAX_POINTERS]; @@ -133,5 +136,3 @@ struct InputTarget { std::string dispatchModeToString(int32_t dispatchMode); } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h index ed5731f8d9..accfc29d8e 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.h +++ b/services/inputflinger/dispatcher/LatencyAggregator.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H -#define _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H +#pragma once #include <kll.h> #include <statslog.h> @@ -86,5 +85,3 @@ private: }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 4b0c618590..64dfeef20d 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H -#define _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H +#pragma once #include <map> #include <unordered_map> @@ -81,5 +80,3 @@ private: }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index 365d5bee4c..7b511915d0 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H -#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H +#pragma once #include <input/InputTransport.h> @@ -30,5 +29,3 @@ struct Monitor { }; } // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 61e78ccbe4..c21af9e0b5 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ +#include <android-base/stringprintf.h> #include <gui/WindowInfo.h> #include "InputTarget.h" - #include "TouchState.h" +using namespace android::ftl::flag_operators; +using android::base::StringPrintf; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; @@ -29,30 +31,31 @@ void TouchState::reset() { *this = TouchState(); } -void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, - BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows[i]; +void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, + std::optional<nsecs_t> eventTime) { + for (TouchedWindow& touchedWindow : windows) { if (touchedWindow.windowHandle == windowHandle) { touchedWindow.targetFlags |= targetFlags; - if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; + if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) { + touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS); } + // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have + // downTime set initially. Need to update existing window when an pointer is down for + // the window. touchedWindow.pointerIds.value |= pointerIds.value; + if (!touchedWindow.firstDownTimeInTarget.has_value()) { + touchedWindow.firstDownTimeInTarget = eventTime; + } return; } } - if (preventNewTargets) return; // Don't add new TouchedWindows. - TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; touchedWindow.targetFlags = targetFlags; touchedWindow.pointerIds = pointerIds; + touchedWindow.firstDownTimeInTarget = eventTime; windows.push_back(touchedWindow); } @@ -68,10 +71,10 @@ void TouchState::removeWindowByToken(const sp<IBinder>& token) { void TouchState::filterNonAsIsTouchWindows() { for (size_t i = 0; i < windows.size();) { TouchedWindow& window = windows[i]; - if (window.targetFlags & - (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { - window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; - window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; + if (window.targetFlags.any(InputTarget::Flags::DISPATCH_AS_IS | + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { + window.targetFlags.clear(InputTarget::DISPATCH_MASK); + window.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS; i += 1; } else { windows.erase(windows.begin() + i); @@ -79,15 +82,31 @@ void TouchState::filterNonAsIsTouchWindows() { } } -void TouchState::filterWindowsExcept(const sp<IBinder>& token) { - std::erase_if(windows, - [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; }); +void TouchState::cancelPointersForWindowsExcept(const BitSet32 pointerIds, + const sp<IBinder>& token) { + if (pointerIds.isEmpty()) return; + std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) { + if (w.windowHandle->getToken() != token) { + w.pointerIds &= BitSet32(~pointerIds.value); + } + }); + std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); }); +} + +void TouchState::cancelPointersForNonPilferingWindows(const BitSet32 pointerIds) { + if (pointerIds.isEmpty()) return; + std::for_each(windows.begin(), windows.end(), [&pointerIds](TouchedWindow& w) { + if (!w.isPilferingPointers) { + w.pointerIds &= BitSet32(~pointerIds.value); + } + }); + std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); }); } sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows[i]; - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { return window.windowHandle; } } @@ -98,7 +117,7 @@ bool TouchState::isSlippery() const { // Must have exactly one foreground window. bool haveSlipperyForegroundWindow = false; for (const TouchedWindow& window : windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { if (haveSlipperyForegroundWindow || !window.windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::SLIPPERY)) { @@ -121,14 +140,25 @@ sp<WindowInfoHandle> TouchState::getWallpaperWindow() const { return nullptr; } -sp<WindowInfoHandle> TouchState::getWindow(const sp<IBinder>& token) const { - for (const TouchedWindow& touchedWindow : windows) { - const auto& windowHandle = touchedWindow.windowHandle; - if (windowHandle->getToken() == token) { - return windowHandle; +bool TouchState::isDown() const { + return std::any_of(windows.begin(), windows.end(), + [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); }); +} + +std::string TouchState::dump() const { + std::string out; + out += StringPrintf("deviceId=%d, source=%s\n", deviceId, + inputEventSourceToString(source).c_str()); + if (!windows.empty()) { + out += " Windows:\n"; + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& touchedWindow = windows[i]; + out += StringPrintf(" %zu : ", i) + touchedWindow.dump(); } + } else { + out += " Windows: <none>\n"; } - return nullptr; + return out; } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 9efb2808c5..77c1cdf50a 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -14,10 +14,8 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H -#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H +#pragma once -#include "Monitor.h" #include "TouchedWindow.h" namespace android { @@ -29,16 +27,10 @@ class WindowInfoHandle; namespace inputdispatcher { struct TouchState { - bool down = false; - bool split = false; - bool preventNewTargets = false; - // id of the device that is currently down, others are rejected int32_t deviceId = -1; // source of the device that is current down, others are rejected uint32_t source = 0; - // id to the display that currently has a touch, others are rejected - int32_t displayId = ADISPLAY_ID_NONE; std::vector<TouchedWindow> windows; @@ -48,17 +40,24 @@ struct TouchState { void reset(); void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds); + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, + std::optional<nsecs_t> eventTime = std::nullopt); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); - void filterWindowsExcept(const sp<IBinder>& token); + + // Cancel pointers for current set of windows except the window with particular binder token. + void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token); + // Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow + // set to false. + void cancelPointersForNonPilferingWindows(const BitSet32 pointerIds); + sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; sp<android::gui::WindowInfoHandle> getWallpaperWindow() const; - sp<android::gui::WindowInfoHandle> getWindow(const sp<IBinder>&) const; + // Whether any of the windows are currently being touched + bool isDown() const; + std::string dump() const; }; } // namespace inputdispatcher } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp new file mode 100644 index 0000000000..af745988ad --- /dev/null +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 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 "TouchedWindow.h" + +#include <android-base/stringprintf.h> +#include <input/PrintTools.h> + +using android::base::StringPrintf; + +namespace android { + +namespace inputdispatcher { + +std::string TouchedWindow::dump() const { + return StringPrintf("name='%s', pointerIds=0x%0x, " + "targetFlags=%s, firstDownTimeInTarget=%s\n", + windowHandle->getName().c_str(), pointerIds.value, + targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str()); +} + +} // namespace inputdispatcher +} // namespace android diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index 6783022ca1..dd08323dd4 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -14,25 +14,27 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H -#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H +#pragma once -namespace android { +#include <gui/WindowInfo.h> +#include <utils/BitSet.h> +#include "InputTarget.h" -namespace gui { -class WindowInfoHandle; -} +namespace android { namespace inputdispatcher { // Focus tracking for touch. struct TouchedWindow { sp<gui::WindowInfoHandle> windowHandle; - int32_t targetFlags; + ftl::Flags<InputTarget::Flags> targetFlags; BitSet32 pointerIds; + bool isPilferingPointers = false; + // Time at which the first action down occurred on this window. + // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario. + std::optional<nsecs_t> firstDownTimeInTarget; + std::string dump() const; }; } // namespace inputdispatcher } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h index 00abf47cd2..5eb3a32ef9 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H +#pragma once #include <utils/Timers.h> @@ -40,5 +39,3 @@ struct InputDispatcherConfiguration { }; } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h index 38d0c32529..5247d8eabb 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H +#pragma once #include <utils/StrongPointer.h> @@ -29,5 +28,3 @@ std::unique_ptr<InputDispatcherInterface> createInputDispatcher( const sp<InputDispatcherPolicyInterface>& policy); } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 67fed8b4f1..76dce63aac 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -14,13 +14,12 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H +#pragma once #include <InputListener.h> #include <android-base/result.h> #include <android/gui/FocusRequest.h> -#include <android/os/BlockUntrustedTouchesMode.h> + #include <android/os/InputEventInjectionResult.h> #include <android/os/InputEventInjectionSync.h> #include <gui/InputApplication.h> @@ -126,13 +125,17 @@ public: /** * Set the touch mode state. - * Touch mode is a global state that apps may enter / exit based on specific - * user interactions with input devices. - * If true, the device is in touch mode. + * Touch mode is a per display state that apps may enter / exit based on specific user + * interactions with input devices. If <code>inTouchMode</code> is set to true, the display + * identified by <code>displayId</code> will be changed to touch mode. Performs a permission + * check if hasPermission is set to false. + * + * This method also enqueues a a TouchModeEntry message for dispatching. * * Returns true when changing touch mode state. */ - virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) = 0; + virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission, + int32_t displayId) = 0; /** * Sets the maximum allowed obscuring opacity by UID to propagate touches. @@ -142,13 +145,6 @@ public: */ virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0; - /** - * Sets the mode of the block untrusted touches feature. - * - * TODO(b/169067926): Clean-up feature modes. - */ - virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) = 0; - /* Transfers touch focus from one window to another window. * * Returns true on success. False if the window did not actually have touch focus. @@ -232,5 +228,3 @@ public: }; } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 575b3d7059..7843923a1f 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H +#pragma once #include "InputDispatcherConfiguration.h" @@ -75,9 +74,6 @@ public: InputDeviceSensorAccuracy accuracy) = 0; virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0; - /* Notifies the system that an untrusted touch occurred. */ - virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0; - /* Gets the input dispatcher configuration. */ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; @@ -143,5 +139,3 @@ public: }; } // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index 2ebdbcfcc4..97d57e4bd0 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -240,19 +240,16 @@ input_property_map_t* InputDriver::inputGetDevicePropertyMap(input_device_identi return nullptr; } -input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, - const char* key) { - String8 keyString(key); +input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) { if (map != nullptr) { - if (map->propertyMap->hasProperty(keyString)) { - auto prop = new input_property_t(); - if (!map->propertyMap->tryGetProperty(keyString, prop->value)) { - delete prop; - return nullptr; - } - prop->key = keyString; - return prop; + std::string value; + auto prop = std::make_unique<input_property_t>(); + if (!map->propertyMap->tryGetProperty(key, value)) { + return nullptr; } + prop->key = key; + prop->value = value.c_str(); + return prop.release(); } return nullptr; } diff --git a/services/inputflinger/host/InputDriver.h b/services/inputflinger/host/InputDriver.h index e56673b4d3..b4c90943a8 100644 --- a/services/inputflinger/host/InputDriver.h +++ b/services/inputflinger/host/InputDriver.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_INPUT_DRIVER_H -#define ANDROID_INPUT_DRIVER_H +#pragma once #include <stdint.h> #include <sys/types.h> @@ -192,4 +191,3 @@ void input_free_device_property_map(input_host_t* host, input_property_map_t* ma } } // namespace android -#endif // ANDROID_INPUT_DRIVER_H diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index 3cf1b2b797..388988bfce 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_INPUT_FLINGER_H -#define ANDROID_INPUT_FLINGER_H +#pragma once #include <stdint.h> #include <sys/types.h> @@ -58,5 +57,3 @@ private: }; } // namespace android - -#endif // ANDROID_INPUT_FLINGER_H diff --git a/services/inputflinger/host/InputHost.h b/services/inputflinger/host/InputHost.h index eda4a89c73..bdc4225738 100644 --- a/services/inputflinger/host/InputHost.h +++ b/services/inputflinger/host/InputHost.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_INPUT_HOST_H -#define ANDROID_INPUT_HOST_H +#pragma once #include <vector> @@ -55,4 +54,3 @@ private: }; } // namespace android -#endif // ANDRIOD_INPUT_HOST_H diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index d9822ce156..1bb19686e7 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -14,237 +14,18 @@ * limitations under the License. */ -#ifndef _UI_INPUT_LISTENER_H -#define _UI_INPUT_LISTENER_H +#pragma once #include <vector> #include <input/Input.h> #include <input/InputDevice.h> #include <input/TouchVideoFrame.h> +#include "NotifyArgs.h" namespace android { -class InputListenerInterface; - - -/* Superclass of all input event argument objects */ -struct NotifyArgs { - int32_t id; - nsecs_t eventTime; - - inline NotifyArgs() : id(0), eventTime(0) {} - - inline explicit NotifyArgs(int32_t id, nsecs_t eventTime) : id(id), eventTime(eventTime) {} - - virtual ~NotifyArgs() { } - - virtual void notify(InputListenerInterface& listener) const = 0; -}; - - -/* Describes a configuration change event. */ -struct NotifyConfigurationChangedArgs : public NotifyArgs { - - inline NotifyConfigurationChangedArgs() { } - - bool operator==(const NotifyConfigurationChangedArgs& rhs) const; - - NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime); - - NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other); - - virtual ~NotifyConfigurationChangedArgs() { } - - void notify(InputListenerInterface& listener) const override; -}; - - -/* Describes a key event. */ -struct NotifyKeyArgs : public NotifyArgs { - int32_t deviceId; - uint32_t source; - int32_t displayId; - uint32_t policyFlags; - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - nsecs_t downTime; - nsecs_t readTime; - - inline NotifyKeyArgs() { } - - NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - nsecs_t downTime); - - bool operator==(const NotifyKeyArgs& rhs) const; - - NotifyKeyArgs(const NotifyKeyArgs& other); - - virtual ~NotifyKeyArgs() { } - - void notify(InputListenerInterface& listener) const override; -}; - - -/* Describes a motion event. */ -struct NotifyMotionArgs : public NotifyArgs { - int32_t deviceId; - uint32_t source; - int32_t displayId; - uint32_t policyFlags; - int32_t action; - int32_t actionButton; - int32_t flags; - int32_t metaState; - int32_t buttonState; - /** - * Classification of the current touch gesture - */ - MotionClassification classification; - int32_t edgeFlags; - - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - float xPrecision; - float yPrecision; - /** - * Mouse cursor position when this event is reported relative to the origin of the specified - * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in - * gestures enabled mode. - */ - float xCursorPosition; - float yCursorPosition; - nsecs_t downTime; - nsecs_t readTime; - std::vector<TouchVideoFrame> videoFrames; - - inline NotifyMotionArgs() { } - - NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames); - - NotifyMotionArgs(const NotifyMotionArgs& other); - - virtual ~NotifyMotionArgs() { } - - bool operator==(const NotifyMotionArgs& rhs) const; - - void notify(InputListenerInterface& listener) const override; - - std::string dump() const; -}; - -/* Describes a sensor event. */ -struct NotifySensorArgs : public NotifyArgs { - int32_t deviceId; - uint32_t source; - InputDeviceSensorType sensorType; - InputDeviceSensorAccuracy accuracy; - bool accuracyChanged; - nsecs_t hwTimestamp; - std::vector<float> values; - - inline NotifySensorArgs() {} - - NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, - InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, - bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values); - - NotifySensorArgs(const NotifySensorArgs& other); - - bool operator==(const NotifySensorArgs rhs) const; - - ~NotifySensorArgs() override {} - - void notify(InputListenerInterface& listener) const override; -}; - -/* Describes a switch event. */ -struct NotifySwitchArgs : public NotifyArgs { - uint32_t policyFlags; - uint32_t switchValues; - uint32_t switchMask; - - inline NotifySwitchArgs() { } - - NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues, - uint32_t switchMask); - - NotifySwitchArgs(const NotifySwitchArgs& other); - - bool operator==(const NotifySwitchArgs rhs) const; - - virtual ~NotifySwitchArgs() { } - - void notify(InputListenerInterface& listener) const override; -}; - - -/* Describes a device reset event, such as when a device is added, - * reconfigured, or removed. */ -struct NotifyDeviceResetArgs : public NotifyArgs { - int32_t deviceId; - - inline NotifyDeviceResetArgs() { } - - NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId); - - NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other); - - bool operator==(const NotifyDeviceResetArgs& rhs) const; - - virtual ~NotifyDeviceResetArgs() { } - - void notify(InputListenerInterface& listener) const override; -}; - -/* Describes a change in the state of Pointer Capture. */ -struct NotifyPointerCaptureChangedArgs : public NotifyArgs { - // The sequence number of the Pointer Capture request, if enabled. - PointerCaptureRequest request; - - inline NotifyPointerCaptureChangedArgs() {} - - NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); - - NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other); - - bool operator==(const NotifyPointerCaptureChangedArgs& rhs) const; - - virtual ~NotifyPointerCaptureChangedArgs() {} - - void notify(InputListenerInterface& listener) const override; -}; - -/* Describes a vibrator state event. */ -struct NotifyVibratorStateArgs : public NotifyArgs { - int32_t deviceId; - bool isOn; - - inline NotifyVibratorStateArgs() {} - - NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn); - - NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other); - - bool operator==(const NotifyVibratorStateArgs rhs) const; - - virtual ~NotifyVibratorStateArgs() {} - - void notify(InputListenerInterface& listener) const override; -}; +std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume); /* * The interface used by the InputReader to notify the InputListener about input events. @@ -264,6 +45,8 @@ public: virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) = 0; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0; virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0; + + void notify(const NotifyArgs& args); }; /* @@ -288,9 +71,7 @@ public: private: InputListenerInterface& mInnerListener; - std::vector<std::unique_ptr<NotifyArgs>> mArgsQueue; + std::vector<NotifyArgs> mArgsQueue; }; } // namespace android - -#endif // _UI_INPUT_LISTENER_H diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 41ecef365d..b8a6dade34 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_READER_BASE_H -#define _UI_INPUT_READER_BASE_H +#pragma once #include <android/os/IInputConstants.h> #include <input/DisplayViewport.h> @@ -24,6 +23,7 @@ #include <input/VelocityControl.h> #include <input/VelocityTracker.h> #include <stddef.h> +#include <ui/Rotation.h> #include <unistd.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -88,6 +88,9 @@ public: virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0; + virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, + int32_t toKeyCode) const = 0; + virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* Toggle Caps Lock */ @@ -95,7 +98,7 @@ public: /* Determine whether physical keys exist for the given framework-domain key codes. */ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; + const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0; /* Requests that a reconfiguration of all input devices. * The changes flag is a bitfield that indicates what has changed and whether @@ -114,6 +117,8 @@ public: virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0; /* Get battery status of a particular input device. */ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0; + /* Get the device path for the battery of an input device. */ + virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0; virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0; @@ -141,6 +146,9 @@ public: virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0; /* Get light player ID */ virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0; + + /* Get the Bluetooth address of an input device, if known. */ + virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0; }; // --- InputReaderConfiguration --- @@ -391,9 +399,9 @@ public: /* Gets the affine calibration associated with the specified device. */ virtual TouchAffineTransformation getTouchAffineTransformation( - const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0; + const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0; + /* Notifies the input reader policy that a stylus gesture has started. */ + virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0; }; } // namespace android - -#endif // _UI_INPUT_READER_COMMON_H diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h index 407365a269..5e75027056 100644 --- a/services/inputflinger/include/InputThread.h +++ b/services/inputflinger/include/InputThread.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_THREAD_H -#define _UI_INPUT_THREAD_H +#pragma once #include <utils/Thread.h> @@ -42,5 +41,3 @@ private: }; } // namespace android - -#endif // _UI_INPUT_THREAD_H
\ No newline at end of file diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h new file mode 100644 index 0000000000..c46f90501f --- /dev/null +++ b/services/inputflinger/include/NotifyArgs.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2022 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 <vector> + +#include <input/Input.h> +#include <input/InputDevice.h> +#include <input/TouchVideoFrame.h> + +namespace android { + +/* Describes a configuration change event. */ +struct NotifyConfigurationChangedArgs { + int32_t id; + nsecs_t eventTime; + + inline NotifyConfigurationChangedArgs() {} + + NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime); + + bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default; + + NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default; +}; + +/* Describes a key event. */ +struct NotifyKeyArgs { + int32_t id; + nsecs_t eventTime; + + int32_t deviceId; + uint32_t source; + int32_t displayId; + uint32_t policyFlags; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + nsecs_t downTime; + nsecs_t readTime; + + inline NotifyKeyArgs() {} + + NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, + uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + nsecs_t downTime); + + bool operator==(const NotifyKeyArgs& rhs) const = default; + + NotifyKeyArgs(const NotifyKeyArgs& other) = default; +}; + +/* Describes a motion event. */ +struct NotifyMotionArgs { + int32_t id; + nsecs_t eventTime; + + int32_t deviceId; + uint32_t source; + int32_t displayId; + uint32_t policyFlags; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + /** + * Classification of the current touch gesture + */ + MotionClassification classification; + int32_t edgeFlags; + + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + float xPrecision; + float yPrecision; + /** + * Mouse cursor position when this event is reported relative to the origin of the specified + * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in + * gestures enabled mode. + */ + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + nsecs_t readTime; + std::vector<TouchVideoFrame> videoFrames; + + inline NotifyMotionArgs() {} + + NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, + uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames); + + NotifyMotionArgs(const NotifyMotionArgs& other); + + NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default; + + bool operator==(const NotifyMotionArgs& rhs) const; + + std::string dump() const; +}; + +/* Describes a sensor event. */ +struct NotifySensorArgs { + int32_t id; + nsecs_t eventTime; + + int32_t deviceId; + uint32_t source; + InputDeviceSensorType sensorType; + InputDeviceSensorAccuracy accuracy; + bool accuracyChanged; + nsecs_t hwTimestamp; + std::vector<float> values; + + inline NotifySensorArgs() {} + + NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, + bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values); + + NotifySensorArgs(const NotifySensorArgs& other) = default; +}; + +/* Describes a switch event. */ +struct NotifySwitchArgs { + int32_t id; + nsecs_t eventTime; + + uint32_t policyFlags; + uint32_t switchValues; + uint32_t switchMask; + + inline NotifySwitchArgs() {} + + NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues, + uint32_t switchMask); + + NotifySwitchArgs(const NotifySwitchArgs& other) = default; + + bool operator==(const NotifySwitchArgs& rhs) const = default; +}; + +/* Describes a device reset event, such as when a device is added, + * reconfigured, or removed. */ +struct NotifyDeviceResetArgs { + int32_t id; + nsecs_t eventTime; + + int32_t deviceId; + + inline NotifyDeviceResetArgs() {} + + NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId); + + NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default; + + bool operator==(const NotifyDeviceResetArgs& rhs) const = default; +}; + +/* Describes a change in the state of Pointer Capture. */ +struct NotifyPointerCaptureChangedArgs { + // The sequence number of the Pointer Capture request, if enabled. + int32_t id; + nsecs_t eventTime; + + PointerCaptureRequest request; + + inline NotifyPointerCaptureChangedArgs() {} + + NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); + + NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default; +}; + +/* Describes a vibrator state event. */ +struct NotifyVibratorStateArgs { + int32_t id; + nsecs_t eventTime; + + int32_t deviceId; + bool isOn; + + inline NotifyVibratorStateArgs() {} + + NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn); + + NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) = default; +}; + +using NotifyArgs = std::variant<NotifyConfigurationChangedArgs, NotifyKeyArgs, NotifyMotionArgs, + NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs, + NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>; + +const char* toString(const NotifyArgs& args); + +} // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index db4228d862..7e0c1c77eb 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H -#define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H +#pragma once #include <input/DisplayViewport.h> #include <input/Input.h> @@ -80,6 +79,8 @@ public: POINTER, // Show spots and a spot anchor in place of the mouse pointer. SPOT, + + ftl_last = SPOT, }; /* Sets the mode of the pointer controller. */ @@ -108,5 +109,3 @@ public: }; } // namespace android - -#endif // _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h index 736041e7fd..04c2e394c8 100644 --- a/services/inputflinger/include/VibrationElement.h +++ b/services/inputflinger/include/VibrationElement.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _VIBRATION_ELEMENT_H -#define _VIBRATION_ELEMENT_H +#pragma once #include <array> #include <chrono> @@ -71,5 +70,3 @@ struct VibrationSequence { }; } // namespace android - -#endif // _VIBRATION_ELEMENT_H diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 01146a3c8b..f37f0fa70e 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -23,6 +23,7 @@ package { cc_library_headers { name: "libinputreader_headers", + host_supported: true, export_include_dirs: [ "controller", "include", @@ -36,11 +37,9 @@ filegroup { srcs: [ "EventHub.cpp", "InputDevice.cpp", + "InputReader.cpp", + "TouchVideoDevice.cpp", "controller/PeripheralController.cpp", - "mapper/accumulator/CursorButtonAccumulator.cpp", - "mapper/accumulator/CursorScrollAccumulator.cpp", - "mapper/accumulator/SingleTouchMotionAccumulator.cpp", - "mapper/accumulator/TouchButtonAccumulator.cpp", "mapper/CursorInputMapper.cpp", "mapper/ExternalStylusInputMapper.cpp", "mapper/InputMapper.cpp", @@ -51,10 +50,17 @@ filegroup { "mapper/SensorInputMapper.cpp", "mapper/SingleTouchInputMapper.cpp", "mapper/SwitchInputMapper.cpp", + "mapper/TouchCursorInputMapperCommon.cpp", "mapper/TouchInputMapper.cpp", + "mapper/TouchpadInputMapper.cpp", "mapper/VibratorInputMapper.cpp", - "InputReader.cpp", - "TouchVideoDevice.cpp", + "mapper/accumulator/CursorButtonAccumulator.cpp", + "mapper/accumulator/CursorScrollAccumulator.cpp", + "mapper/accumulator/HidUsageAccumulator.cpp", + "mapper/accumulator/MultiTouchMotionAccumulator.cpp", + "mapper/accumulator/SingleTouchMotionAccumulator.cpp", + "mapper/accumulator/TouchButtonAccumulator.cpp", + "mapper/gestures/GesturesLogging.cpp", ], } @@ -66,24 +72,59 @@ cc_defaults { "libcap", "libcrypto", "libcutils", - "libinput", + "libjsoncpp", "liblog", + "libPlatformProperties", "libstatslog", - "libui", "libutils", - "libPlatformProperties", ], static_libs: [ "libc++fs", + "libchrome-gestures", + "libui-types", ], header_libs: [ "libbatteryservice_headers", + "libchrome-gestures_headers", "libinputreader_headers", ], + target: { + android: { + shared_libs: [ + "libinput", + ], + }, + host: { + static_libs: [ + "libinput", + "libbinder", + ], + }, + }, +} + +cc_library_static { + name: "libinputreader_static", + defaults: [ + "inputflinger_defaults", + "libinputreader_defaults", + ], + shared_libs: [ + "libinputflinger_base", + ], + export_header_lib_headers: [ + "libbatteryservice_headers", + "libchrome-gestures_headers", + "libinputreader_headers", + ], + whole_static_libs: [ + "libchrome-gestures", + ], } cc_library_shared { name: "libinputreader", + host_supported: true, defaults: [ "inputflinger_defaults", "libinputreader_defaults", @@ -95,11 +136,21 @@ cc_library_shared { // This should consist only of dependencies from inputflinger. Other dependencies should be // in cc_defaults so that they are included in the tests. "libinputflinger_base", + "libjsoncpp", ], export_header_lib_headers: [ "libinputreader_headers", ], + target: { + host: { + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi", + ], + }, + }, static_libs: [ "libc++fs", + "libchrome-gestures", ], } diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 20baa42a20..e26bc8c6dc 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -19,6 +19,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <linux/ioctl.h> #include <memory.h> #include <stdint.h> #include <stdio.h> @@ -28,7 +29,6 @@ #include <sys/epoll.h> #include <sys/inotify.h> #include <sys/ioctl.h> -#include <sys/limits.h> #include <sys/stat.h> #include <sys/sysmacros.h> #include <unistd.h> @@ -43,6 +43,7 @@ #include <ftl/enum.h> #include <input/KeyCharacterMap.h> #include <input/KeyLayoutMap.h> +#include <input/PrintTools.h> #include <input/VirtualKeyMap.h> #include <openssl/sha.h> #include <statslog.h> @@ -52,6 +53,7 @@ #include <filesystem> #include <regex> +#include <utility> #include "EventHub.h" @@ -60,6 +62,7 @@ #define INDENT3 " " using android::base::StringPrintf; +using android::hardware::input::InputDeviceCountryCode; namespace android { @@ -74,6 +77,8 @@ static constexpr size_t OBFUSCATED_LENGTH = 8; static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0; static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1; +static constexpr size_t EVENT_BUFFER_SIZE = 256; + // Mapping for input battery class node IDs lookup. // https://www.kernel.org/doc/Documentation/power/power_supply_class.txt static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES = @@ -115,7 +120,8 @@ static const std::unordered_map<std::string, InputLightClass> LIGHT_CLASSES = {"brightness", InputLightClass::BRIGHTNESS}, {"multi_index", InputLightClass::MULTI_INDEX}, {"multi_intensity", InputLightClass::MULTI_INTENSITY}, - {"max_brightness", InputLightClass::MAX_BRIGHTNESS}}; + {"max_brightness", InputLightClass::MAX_BRIGHTNESS}, + {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}}; // Mapping for input multicolor led class node names. // https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html @@ -129,10 +135,6 @@ const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightC {"green", LightColor::GREEN}, {"blue", LightColor::BLUE}}; -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - static std::string sha1(const std::string& in) { SHA_CTX ctx; SHA1_Init(&ctx); @@ -147,6 +149,14 @@ static std::string sha1(const std::string& in) { return out; } +/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */ +static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = { + AKEYCODE_STYLUS_BUTTON_PRIMARY, + AKEYCODE_STYLUS_BUTTON_SECONDARY, + AKEYCODE_STYLUS_BUTTON_TERTIARY, + AKEYCODE_STYLUS_BUTTON_TAIL, +}; + /** * Return true if name matches "v4l-touch*" */ @@ -187,14 +197,13 @@ static nsecs_t processEventTimestamp(const struct input_event& event) { // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). - const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) + - microseconds_to_nanoseconds(event.time.tv_usec); + const nsecs_t inputEventTime = seconds_to_nanoseconds(event.input_event_sec) + + microseconds_to_nanoseconds(event.input_event_usec); return inputEventTime; } /** - * Returns the sysfs root path of the input device - * + * Returns the sysfs root path of the input device. */ static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) { std::error_code errorCode; @@ -301,6 +310,112 @@ static std::optional<std::array<LightColor, COLOR_NUM>> getColorIndexArray( return colors; } +/** + * Read country code information exposed through the sysfs path. + */ +static InputDeviceCountryCode readCountryCodeLocked(const std::filesystem::path& sysfsRootPath) { + // Check the sysfs root path + int hidCountryCode = static_cast<int>(InputDeviceCountryCode::INVALID); + std::string str; + if (base::ReadFileToString(sysfsRootPath / "country", &str)) { + hidCountryCode = std::stoi(str, nullptr, 16); + LOG_ALWAYS_FATAL_IF(hidCountryCode > 35 || hidCountryCode < 0, + "HID country code should be in range [0, 35]. Found country code " + "to be %d", + hidCountryCode); + } + + return static_cast<InputDeviceCountryCode>(hidCountryCode); +} + +/** + * Read information about batteries exposed through the sysfs path. + */ +static std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> readBatteryConfiguration( + const std::filesystem::path& sysfsRootPath) { + std::unordered_map<int32_t, RawBatteryInfo> batteryInfos; + int32_t nextBatteryId = 0; + // Check if device has any battery. + const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY); + for (const auto& nodePath : paths) { + RawBatteryInfo info; + info.id = ++nextBatteryId; + info.path = nodePath; + info.name = nodePath.filename(); + + // Scan the path for all the files + // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt + const auto& files = allFilesInPath(nodePath); + for (const auto& file : files) { + const auto it = BATTERY_CLASSES.find(file.filename().string()); + if (it != BATTERY_CLASSES.end()) { + info.flags |= it->second; + } + } + batteryInfos.insert_or_assign(info.id, info); + ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str()); + } + return batteryInfos; +} + +/** + * Read information about lights exposed through the sysfs path. + */ +static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration( + const std::filesystem::path& sysfsRootPath) { + std::unordered_map<int32_t, RawLightInfo> lightInfos; + int32_t nextLightId = 0; + // Check if device has any lights. + const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS); + for (const auto& nodePath : paths) { + RawLightInfo info; + info.id = ++nextLightId; + info.path = nodePath; + info.name = nodePath.filename(); + info.maxBrightness = std::nullopt; + + // Light name should follow the naming pattern <name>:<color>:<function> + // Refer kernel docs /leds/leds-class.html for valid supported LED names. + std::regex indexPattern("([a-zA-Z0-9_.:]*:)?([a-zA-Z0-9_.]*):([a-zA-Z0-9_.]*)"); + std::smatch results; + + if (std::regex_match(info.name, results, indexPattern)) { + // regex_match will return full match at index 0 and <name> at index 1. For RawLightInfo + // we only care about sections <color> and <function> which will be at index 2 and 3. + for (int i = 2; i <= 3; i++) { + const auto it = LIGHT_CLASSES.find(results.str(i)); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + } + } + + // Set name of the raw light to <function> which represents playerIDs for LEDs that + // turn on/off based on the current player ID (Refer to PeripheralController.cpp for + // player ID logic) + info.name = results.str(3); + } + // Scan the path for all the files + // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt + const auto& files = allFilesInPath(nodePath); + for (const auto& file : files) { + const auto it = LIGHT_CLASSES.find(file.filename().string()); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + // If the node has maximum brightness, read it + if (it->second == InputLightClass::MAX_BRIGHTNESS) { + std::string str; + if (base::ReadFileToString(file, &str)) { + info.maxBrightness = std::stoi(str); + } + } + } + } + lightInfos.insert_or_assign(info.id, info); + ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str()); + } + return lightInfos; +} + // --- Global Functions --- ftl::Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, @@ -357,18 +472,18 @@ ftl::Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, // --- EventHub::Device --- -EventHub::Device::Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier) +EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier, + std::shared_ptr<const AssociatedDevice> assocDev) : fd(fd), id(id), - path(path), - identifier(identifier), + path(std::move(path)), + identifier(std::move(identifier)), classes(0), configuration(nullptr), virtualKeyMap(nullptr), ffEffectPlaying(false), ffEffectId(-1), - associatedDevice(nullptr), + associatedDevice(std::move(assocDev)), controllerNumber(0), enabled(true), isVirtual(fd < 0) {} @@ -502,7 +617,7 @@ status_t EventHub::Device::loadKeyMapLocked() { bool EventHub::Device::isExternalDeviceLocked() { if (configuration) { bool value; - if (configuration->tryGetProperty(String8("device.internal"), value)) { + if (configuration->tryGetProperty("device.internal", value)) { return !value; } } @@ -512,7 +627,7 @@ bool EventHub::Device::isExternalDeviceLocked() { bool EventHub::Device::deviceHasMicLocked() { if (configuration) { bool value; - if (configuration->tryGetProperty(String8("audio.mic"), value)) { + if (configuration->tryGetProperty("audio.mic", value)) { return value; } } @@ -523,8 +638,8 @@ void EventHub::Device::setLedStateLocked(int32_t led, bool on) { int32_t sc; if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) { struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; + ev.input_event_sec = 0; + ev.input_event_usec = 0; ev.type = EV_LED; ev.code = sc; ev.value = on ? 1 : 0; @@ -557,75 +672,6 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } -// Check the sysfs path for any input device batteries, returns true if battery found. -bool EventHub::AssociatedDevice::configureBatteryLocked() { - nextBatteryId = 0; - // Check if device has any battery. - const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY); - for (const auto& nodePath : paths) { - RawBatteryInfo info; - info.id = ++nextBatteryId; - info.path = nodePath; - info.name = nodePath.filename(); - - // Scan the path for all the files - // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt - const auto& files = allFilesInPath(nodePath); - for (const auto& file : files) { - const auto it = BATTERY_CLASSES.find(file.filename().string()); - if (it != BATTERY_CLASSES.end()) { - info.flags |= it->second; - } - } - batteryInfos.insert_or_assign(info.id, info); - ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str()); - } - return !batteryInfos.empty(); -} - -// Check the sysfs path for any input device lights, returns true if lights found. -bool EventHub::AssociatedDevice::configureLightsLocked() { - nextLightId = 0; - // Check if device has any lights. - const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS); - for (const auto& nodePath : paths) { - RawLightInfo info; - info.id = ++nextLightId; - info.path = nodePath; - info.name = nodePath.filename(); - info.maxBrightness = std::nullopt; - size_t nameStart = info.name.rfind(":"); - if (nameStart != std::string::npos) { - // Trim the name to color name - info.name = info.name.substr(nameStart + 1); - // Set InputLightClass flag for colors - const auto it = LIGHT_CLASSES.find(info.name); - if (it != LIGHT_CLASSES.end()) { - info.flags |= it->second; - } - } - // Scan the path for all the files - // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt - const auto& files = allFilesInPath(nodePath); - for (const auto& file : files) { - const auto it = LIGHT_CLASSES.find(file.filename().string()); - if (it != LIGHT_CLASSES.end()) { - info.flags |= it->second; - // If the node has maximum brightness, read it - if (it->second == InputLightClass::MAX_BRIGHTNESS) { - std::string str; - if (base::ReadFileToString(file, &str)) { - info.maxBrightness = std::stoi(str); - } - } - } - } - lightInfos.insert_or_assign(info.id, info); - ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str()); - } - return !lightInfos.empty(); -} - /** * Get the capabilities for the current process. * Crashes the system if unable to create / check / destroy the capabilities object. @@ -687,6 +733,7 @@ EventHub::EventHub(void) LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); mINotifyFd = inotify_init1(IN_CLOEXEC); + LOG_ALWAYS_FATAL_IF(mINotifyFd < 0, "Could not create inotify instance: %s", strerror(errno)); std::error_code errorCode; bool isDeviceInotifyAdded = false; @@ -905,15 +952,19 @@ int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKey device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode); switch (mapKeyRes) { case OK: - return outKeyCode; + break; case NAME_NOT_FOUND: // key character map doesn't re-map this scanCode, hence the keyCode remains the same - return locationKeyCode; + outKeyCode = locationKeyCode; + break; default: ALOGW("Failed to get key code for key location: Key character map returned error %s", statusToString(mapKeyRes).c_str()); - return AKEYCODE_UNKNOWN; + outKeyCode = AKEYCODE_UNKNOWN; + break; } + // Remap if there is a Key remapping added to the KCM and return the remapped key + return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode); } int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { @@ -952,20 +1003,20 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* return -1; } -bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, +bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + for (size_t codeIndex = 0; codeIndex < keyCodes.size(); codeIndex++) { std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { + for (const int32_t scanCode : scanCodes) { + if (device->keyBitmask.test(scanCode)) { outFlags[codeIndex] = 1; break; } @@ -976,6 +1027,18 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in return false; } +void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + return; + } + const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap(); + if (kcm) { + kcm->addKeyRemapping(fromKeyCode, toKeyCode); + } +} + status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { std::scoped_lock _l(mLock); @@ -1001,7 +1064,13 @@ status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, if (status == NO_ERROR) { if (kcm) { - kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState); + // Remap keys based on user-defined key remappings and key behavior defined in the + // corresponding kcm file + *outKeycode = kcm->applyKeyRemapping(*outKeycode); + + // Remap keys based on Key behavior defined in KCM file + std::tie(*outKeycode, *outMetaState) = + kcm->applyKeyBehavior(*outKeycode, metaState); } else { *outMetaState = metaState; } @@ -1033,7 +1102,7 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis } base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId, - int32_t absCode) { + int32_t absCode) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); @@ -1055,18 +1124,19 @@ const std::unordered_map<int32_t, RawBatteryInfo>& EventHub::getBatteryInfoLocke return device->associatedDevice->batteryInfos; } -const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) { +std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) const { std::scoped_lock _l(mLock); std::vector<int32_t> batteryIds; - for (const auto [id, info] : getBatteryInfoLocked(deviceId)) { + for (const auto& [id, info] : getBatteryInfoLocked(deviceId)) { batteryIds.push_back(id); } return batteryIds; } -std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) { +std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, + int32_t batteryId) const { std::scoped_lock _l(mLock); const auto infos = getBatteryInfoLocked(deviceId); @@ -1080,7 +1150,7 @@ std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int3 } // Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated -// with the deivice ID. Returns an empty map if no miscellaneous device found. +// with the device ID. Returns an empty map if no miscellaneous device found. const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked( int32_t deviceId) const { static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {}; @@ -1091,18 +1161,18 @@ const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked( return device->associatedDevice->lightInfos; } -const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) { +std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) const { std::scoped_lock _l(mLock); std::vector<int32_t> lightIds; - for (const auto [id, info] : getLightInfoLocked(deviceId)) { + for (const auto& [id, info] : getLightInfoLocked(deviceId)) { lightIds.push_back(id); } return lightIds; } -std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) { +std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const { std::scoped_lock _l(mLock); const auto infos = getLightInfoLocked(deviceId); @@ -1115,7 +1185,7 @@ std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t return std::nullopt; } -std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) { +std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const { std::scoped_lock _l(mLock); const auto infos = getLightInfoLocked(deviceId); @@ -1132,7 +1202,7 @@ std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t li } std::optional<std::unordered_map<LightColor, int32_t>> EventHub::getLightIntensities( - int32_t deviceId, int32_t lightId) { + int32_t deviceId, int32_t lightId) const { std::scoped_lock _l(mLock); const auto infos = getLightInfoLocked(deviceId); @@ -1228,6 +1298,15 @@ void EventHub::setLightIntensities(int32_t deviceId, int32_t lightId, } } +InputDeviceCountryCode EventHub::getCountryCode(int32_t deviceId) const { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr || !device->associatedDevice) { + return InputDeviceCountryCode::INVALID; + } + return device->associatedDevice->countryCode; +} + void EventHub::setExcludedDevices(const std::vector<std::string>& devices) { std::scoped_lock _l(mLock); @@ -1357,6 +1436,39 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { identifier.descriptor.c_str()); } +std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked( + const std::filesystem::path& devicePath) const { + const std::optional<std::filesystem::path> sysfsRootPathOpt = + getSysfsRootPath(devicePath.c_str()); + if (!sysfsRootPathOpt) { + return nullptr; + } + + const auto& path = *sysfsRootPathOpt; + + std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>( + AssociatedDevice{.sysfsRootPath = path, + .countryCode = readCountryCodeLocked(path), + .batteryInfos = readBatteryConfiguration(path), + .lightInfos = readLightsConfiguration(path)}); + + bool associatedDeviceChanged = false; + for (const auto& [id, dev] : mDevices) { + if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) { + if (*associatedDevice != *dev->associatedDevice) { + associatedDeviceChanged = true; + dev->associatedDevice = associatedDevice; + } + associatedDevice = dev->associatedDevice; + } + } + ALOGI_IF(associatedDeviceChanged, + "The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s", + path.c_str(), associatedDevice->dump().c_str()); + + return associatedDevice; +} + void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); @@ -1378,8 +1490,8 @@ void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) { device->ffEffectId = effect.id; struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; + ev.input_event_sec = 0; + ev.input_event_usec = 0; ev.type = EV_FF; ev.code = device->ffEffectId; ev.value = 1; @@ -1400,8 +1512,8 @@ void EventHub::cancelVibrate(int32_t deviceId) { device->ffEffectPlaying = false; struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; + ev.input_event_sec = 0; + ev.input_event_usec = 0; ev.type = EV_FF; ev.code = device->ffEffectId; ev.value = 0; @@ -1414,7 +1526,7 @@ void EventHub::cancelVibrate(int32_t deviceId) { } } -std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) { +std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) const { std::scoped_lock _l(mLock); std::vector<int32_t> vibrators; Device* device = getDeviceLocked(deviceId); @@ -1495,7 +1607,7 @@ std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t ba // the lock to prevent event processing from being blocked by this call. std::scoped_lock _l(mLock); - const auto infos = getBatteryInfoLocked(deviceId); + const auto& infos = getBatteryInfoLocked(deviceId); auto it = infos.find(batteryId); if (it == infos.end()) { return std::nullopt; @@ -1536,7 +1648,7 @@ std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batt // the lock to prevent event processing from being blocked by this call. std::scoped_lock _l(mLock); - const auto infos = getBatteryInfoLocked(deviceId); + const auto& infos = getBatteryInfoLocked(deviceId); auto it = infos.find(batteryId); if (it == infos.end()) { return std::nullopt; @@ -1561,15 +1673,12 @@ std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batt return std::nullopt; } -size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { - ALOG_ASSERT(bufferSize >= 1); - +std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { std::scoped_lock _l(mLock); - struct input_event readBuffer[bufferSize]; + std::array<input_event, EVENT_BUFFER_SIZE> readBuffer; - RawEvent* event = buffer; - size_t capacity = bufferSize; + std::vector<RawEvent> events; bool awoken = false; for (;;) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -1589,15 +1698,17 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) { std::unique_ptr<Device> device = std::move(*it); ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str()); - event->when = now; - event->deviceId = (device->id == mBuiltInKeyboardId) + const int32_t deviceId = (device->id == mBuiltInKeyboardId) ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id; - event->type = DEVICE_REMOVED; - event += 1; + events.push_back({ + .when = now, + .deviceId = deviceId, + .type = DEVICE_REMOVED, + }); it = mClosingDevices.erase(it); mNeedToSendFinishedDeviceScan = true; - if (--capacity == 0) { + if (events.size() == EVENT_BUFFER_SIZE) { break; } } @@ -1612,10 +1723,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin()); mOpeningDevices.pop_back(); ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str()); - event->when = now; - event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; - event->type = DEVICE_ADDED; - event += 1; + const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + events.push_back({ + .when = now, + .deviceId = deviceId, + .type = DEVICE_ADDED, + }); // Try to find a matching video device by comparing device names for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end(); @@ -1633,17 +1746,18 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz ALOGW("Device id %d exists, replaced.", device->id); } mNeedToSendFinishedDeviceScan = true; - if (--capacity == 0) { + if (events.size() == EVENT_BUFFER_SIZE) { break; } } if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; - event->when = now; - event->type = FINISHED_DEVICE_SCAN; - event += 1; - if (--capacity == 0) { + events.push_back({ + .when = now, + .type = FINISHED_DEVICE_SCAN, + }); + if (events.size() == EVENT_BUFFER_SIZE) { break; } } @@ -1707,12 +1821,13 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz // This must be an input event if (eventItem.events & EPOLLIN) { int32_t readSize = - read(device->fd, readBuffer, sizeof(struct input_event) * capacity); + read(device->fd, readBuffer.data(), + sizeof(decltype(readBuffer)::value_type) * readBuffer.size()); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. ALOGW("could not get event, removed? (fd: %d size: %" PRId32 - " bufferSize: %zu capacity: %zu errno: %d)\n", - device->fd, readSize, bufferSize, capacity, errno); + " capacity: %zu errno: %d)\n", + device->fd, readSize, readBuffer.size(), errno); deviceChanged = true; closeDeviceLocked(*device); } else if (readSize < 0) { @@ -1722,21 +1837,21 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } else if ((readSize % sizeof(struct input_event)) != 0) { ALOGE("could not get event (wrong size: %d)", readSize); } else { - int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; - size_t count = size_t(readSize) / sizeof(struct input_event); + const size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { struct input_event& iev = readBuffer[i]; - event->when = processEventTimestamp(iev); - event->readTime = systemTime(SYSTEM_TIME_MONOTONIC); - event->deviceId = deviceId; - event->type = iev.type; - event->code = iev.code; - event->value = iev.value; - event += 1; - capacity -= 1; + events.push_back({ + .when = processEventTimestamp(iev), + .readTime = systemTime(SYSTEM_TIME_MONOTONIC), + .deviceId = deviceId, + .type = iev.type, + .code = iev.code, + .value = iev.value, + }); } - if (capacity == 0) { + if (events.size() >= EVENT_BUFFER_SIZE) { // The result buffer is full. Reset the pending event index // so we will try to read the device again on the next iteration. mPendingEventIndex -= 1; @@ -1759,7 +1874,10 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz // before closing the devices. if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; - readNotifyLocked(); + const auto res = readNotifyLocked(); + if (!res.ok()) { + ALOGW("Failed to read from inotify: %s", res.error().message().c_str()); + } deviceChanged = true; } @@ -1769,7 +1887,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } // Return now if we have collected any events or if we were explicitly awoken. - if (event != buffer || awoken) { + if (!events.empty() || awoken) { break; } @@ -1815,7 +1933,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } // All done, return the number of events we read. - return event - buffer; + return events; } std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) { @@ -2037,12 +2155,25 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { identifier.uniqueId = buffer; } + // Attempt to get the bluetooth address of an input device from the uniqueId. + if (identifier.bus == BUS_BLUETOOTH && + std::regex_match(identifier.uniqueId, + std::regex("^[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}$"))) { + identifier.bluetoothAddress = identifier.uniqueId; + // The Bluetooth stack requires alphabetic characters to be uppercase in a valid address. + for (auto& c : *identifier.bluetoothAddress) { + c = ::toupper(c); + } + } + // Fill in the descriptor. assignDescriptorLocked(identifier); // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; - std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier); + std::unique_ptr<Device> device = + std::make_unique<Device>(fd, deviceId, devicePath, identifier, + obtainAssociatedDeviceLocked(devicePath)); ALOGV("add device %d: %s\n", deviceId, devicePath.c_str()); ALOGV(" bus: %04x\n" @@ -2060,27 +2191,6 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { // Load the configuration file for the device. device->loadConfigurationLocked(); - bool hasBattery = false; - bool hasLights = false; - // Check the sysfs root path - std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str()); - if (sysfsRootPath.has_value()) { - std::shared_ptr<AssociatedDevice> associatedDevice; - for (const auto& [id, dev] : mDevices) { - if (device->identifier.descriptor == dev->identifier.descriptor && - !dev->associatedDevice) { - associatedDevice = dev->associatedDevice; - } - } - if (!associatedDevice) { - associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value()); - } - hasBattery = associatedDevice->configureBatteryLocked(); - hasLights = associatedDevice->configureLightsLocked(); - - device->associatedDevice = associatedDevice; - } - // Figure out the kinds of events the device reports. device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask); device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask); @@ -2091,13 +2201,15 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask); device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask); - // See if this is a keyboard. Ignore everything in the button range except for - // joystick and gamepad buttons which are handled like keyboards for the most part. + // See if this is a device with keys. This could be full keyboard, or other devices like + // gamepads, joysticks, and styluses with buttons that should generate key presses. bool haveKeyboardKeys = device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1); bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) || device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI); - if (haveKeyboardKeys || haveGamepadButtons) { + bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) || + device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3); + if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) { device->classes |= InputDeviceClass::KEYBOARD; } @@ -2107,12 +2219,13 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { device->classes |= InputDeviceClass::CURSOR; } - // See if this is a rotary encoder type device. - String8 deviceType = String8(); - if (device->configuration && - device->configuration->tryGetProperty(String8("device.type"), deviceType)) { - if (!deviceType.compare(String8("rotaryEncoder"))) { + // See if the device is specially configured to be of a certain type. + std::string deviceType; + if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) { + if (deviceType == "rotaryEncoder") { device->classes |= InputDeviceClass::ROTARY_ENCODER; + } else if (deviceType == "externalStylus") { + device->classes |= InputDeviceClass::EXTERNAL_STYLUS; } } @@ -2124,19 +2237,19 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { // a touch screen. if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) { device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT); + if (device->propBitmask.test(INPUT_PROP_POINTER) && + !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) { + device->classes |= InputDeviceClass::TOUCHPAD; + } } // Is this an old style single-touch driver? } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) && device->absBitmask.test(ABS_Y)) { device->classes |= InputDeviceClass::TOUCH; - // Is this a BT stylus? + // Is this a stylus that reports contact/pressure independently of touch coordinates? } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) && !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) { device->classes |= InputDeviceClass::EXTERNAL_STYLUS; - // Keyboard will try to claim some of the buttons but we really want to reserve those so we - // can fuse it with the touch screen data, so just take them back. Note this means an - // external stylus cannot also be a keyboard device. - device->classes &= ~InputDeviceClass::KEYBOARD; } // See if this device is a joystick. @@ -2221,6 +2334,16 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { break; } } + + // See if this device has any stylus buttons that we would want to fuse with touch data. + if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) { + for (int32_t keycode : STYLUS_BUTTON_KEYCODES) { + if (device->hasKeycodeLocked(keycode)) { + device->classes |= InputDeviceClass::EXTERNAL_STYLUS; + break; + } + } + } } // If the device isn't recognized as something we handle, don't monitor it. @@ -2231,12 +2354,12 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { } // Classify InputDeviceClass::BATTERY. - if (hasBattery) { + if (device->associatedDevice && !device->associatedDevice->batteryInfos.empty()) { device->classes |= InputDeviceClass::BATTERY; } // Classify InputDeviceClass::LIGHT. - if (hasLights) { + if (device->associatedDevice && !device->associatedDevice->lightInfos.empty()) { device->classes |= InputDeviceClass::LIGHT; } @@ -2304,7 +2427,7 @@ bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device, return true; } -bool EventHub::isDeviceEnabled(int32_t deviceId) { +bool EventHub::isDeviceEnabled(int32_t deviceId) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device == nullptr) { @@ -2359,7 +2482,7 @@ void EventHub::createVirtualKeyboardLocked() { std::unique_ptr<Device> device = std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", - identifier); + identifier, nullptr /*associatedDevice*/); device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY | InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL; device->loadKeyMapLocked(); @@ -2449,53 +2572,56 @@ void EventHub::closeDeviceLocked(Device& device) { mDevices.erase(device.id); } -status_t EventHub::readNotifyLocked() { - int res; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event* event; +base::Result<void> EventHub::readNotifyLocked() { + static constexpr auto EVENT_SIZE = static_cast<ssize_t>(sizeof(inotify_event)); + uint8_t eventBuffer[512]; + ssize_t sizeRead; ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); - res = read(mINotifyFd, event_buf, sizeof(event_buf)); - if (res < (int)sizeof(*event)) { - if (errno == EINTR) return 0; - ALOGW("could not get event, %s\n", strerror(errno)); - return -1; - } - - while (res >= (int)sizeof(*event)) { - event = (struct inotify_event*)(event_buf + event_pos); - if (event->len) { - if (event->wd == mDeviceInputWd) { - std::string filename = std::string(DEVICE_INPUT_PATH) + "/" + event->name; - if (event->mask & IN_CREATE) { - openDeviceLocked(filename); - } else { - ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); - closeDeviceByPathLocked(filename); - } - } else if (event->wd == mDeviceWd) { - if (isV4lTouchNode(event->name)) { - std::string filename = std::string(DEVICE_PATH) + "/" + event->name; - if (event->mask & IN_CREATE) { - openVideoDeviceLocked(filename); - } else { - ALOGI("Removing video device '%s' due to inotify event", filename.c_str()); - closeVideoDeviceByPathLocked(filename); - } - } else if (strcmp(event->name, "input") == 0 && event->mask & IN_CREATE) { - addDeviceInputInotify(); - } + do { + sizeRead = read(mINotifyFd, eventBuffer, sizeof(eventBuffer)); + } while (sizeRead < 0 && errno == EINTR); + + if (sizeRead < EVENT_SIZE) return Errorf("could not get event, %s", strerror(errno)); + + for (ssize_t eventPos = 0; sizeRead >= EVENT_SIZE;) { + const inotify_event* event; + event = (const inotify_event*)(eventBuffer + eventPos); + if (event->len == 0) continue; + + handleNotifyEventLocked(*event); + + const ssize_t eventSize = EVENT_SIZE + event->len; + sizeRead -= eventSize; + eventPos += eventSize; + } + return {}; +} + +void EventHub::handleNotifyEventLocked(const inotify_event& event) { + if (event.wd == mDeviceInputWd) { + std::string filename = std::string(DEVICE_INPUT_PATH) + "/" + event.name; + if (event.mask & IN_CREATE) { + openDeviceLocked(filename); + } else { + ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); + closeDeviceByPathLocked(filename); + } + } else if (event.wd == mDeviceWd) { + if (isV4lTouchNode(event.name)) { + std::string filename = std::string(DEVICE_PATH) + "/" + event.name; + if (event.mask & IN_CREATE) { + openVideoDeviceLocked(filename); } else { - LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd); + ALOGI("Removing video device '%s' due to inotify event", filename.c_str()); + closeVideoDeviceByPathLocked(filename); } + } else if (strcmp(event.name, "input") == 0 && event.mask & IN_CREATE) { + addDeviceInputInotify(); } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; + } else { + LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event.wd); } - return 0; } status_t EventHub::scanDirLocked(const std::string& dirname) { @@ -2525,7 +2651,7 @@ void EventHub::requestReopenDevices() { mNeedToReopenDevices = true; } -void EventHub::dump(std::string& dump) { +void EventHub::dump(std::string& dump) const { dump += "Event Hub State:\n"; { // acquire lock @@ -2551,21 +2677,26 @@ void EventHub::dump(std::string& dump) { dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber); dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str()); dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " - "product=0x%04x, version=0x%04x\n", + "product=0x%04x, version=0x%04x, bluetoothAddress=%s\n", device->identifier.bus, device->identifier.vendor, - device->identifier.product, device->identifier.version); + device->identifier.product, device->identifier.version, + toString(device->identifier.bluetoothAddress).c_str()); dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n", device->keyMap.keyLayoutFile.c_str()); dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", device->keyMap.keyCharacterMapFile.c_str()); + dump += StringPrintf(INDENT3 "CountryCode: %d\n", + device->associatedDevice ? device->associatedDevice->countryCode + : InputDeviceCountryCode::INVALID); dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", device->configurationFile.c_str()); - dump += INDENT3 "VideoDevice: "; - if (device->videoDevice) { - dump += device->videoDevice->dump() + "\n"; - } else { - dump += "<none>\n"; - } + dump += StringPrintf(INDENT3 "VideoDevice: %s\n", + device->videoDevice ? device->videoDevice->dump().c_str() + : "<none>"); + dump += StringPrintf(INDENT3 "SysfsDevicePath: %s\n", + device->associatedDevice + ? device->associatedDevice->sysfsRootPath.c_str() + : "<none>"); } dump += INDENT "Unattached video devices:\n"; @@ -2578,9 +2709,14 @@ void EventHub::dump(std::string& dump) { } // release lock } -void EventHub::monitor() { +void EventHub::monitor() const { // Acquire and release the lock to ensure that the event hub has not deadlocked. std::unique_lock<std::mutex> lock(mLock); } -}; // namespace android +std::string EventHub::AssociatedDevice::dump() const { + return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(), + batteryInfos.size(), lightInfos.size()); +} + +} // namespace android diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 3f26aaaebe..11b5209b0f 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -20,6 +20,7 @@ #include <algorithm> +#include <android/sysprop/InputProperties.sysprop.h> #include <ftl/flags.h> #include "CursorInputMapper.h" @@ -33,8 +34,11 @@ #include "SensorInputMapper.h" #include "SingleTouchInputMapper.h" #include "SwitchInputMapper.h" +#include "TouchpadInputMapper.h" #include "VibratorInputMapper.h" +using android::hardware::input::InputDeviceCountryCode; + namespace android { InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, @@ -63,7 +67,8 @@ bool InputDevice::isEnabled() { return enabled; } -void InputDevice::setEnabled(bool enabled, nsecs_t when) { +std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) { + std::list<NotifyArgs> out; if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " "but the corresponding viewport is not found", @@ -72,7 +77,7 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { } if (isEnabled() == enabled) { - return; + return out; } // When resetting some devices, the driver needs to be queried to ensure that a proper reset is @@ -80,13 +85,14 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information. if (enabled) { for_each_subdevice([](auto& context) { context.enableDevice(); }); - reset(when); + out += reset(when); } else { - reset(when); + out += reset(when); for_each_subdevice([](auto& context) { context.disableDevice(); }); } // Must change generation to flag this device as changed bumpGeneration(); + return out; } void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { @@ -204,7 +210,12 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { } // Touchscreens and touchpad devices. - if (classes.test(InputDeviceClass::TOUCH_MT)) { + static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY = + sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false); + if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) && + classes.test(InputDeviceClass::TOUCH_MT)) { + mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr)); + } else if (classes.test(InputDeviceClass::TOUCH_MT)) { mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr)); } else if (classes.test(InputDeviceClass::TOUCH)) { mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr)); @@ -239,11 +250,13 @@ void InputDevice::removeEventHubDevice(int32_t eventHubId) { mDevices.erase(eventHubId); } -void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { +std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out; mSources = 0; mClasses = ftl::Flags<InputDeviceClass>(0); mControllerNumber = 0; + mCountryCode = InputDeviceCountryCode::INVALID; for_each_subdevice([this](InputDeviceContext& context) { mClasses |= context.getDeviceClasses(); @@ -255,6 +268,16 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } mControllerNumber = controllerNumber; } + + InputDeviceCountryCode countryCode = context.getCountryCode(); + if (countryCode != InputDeviceCountryCode::INVALID) { + if (mCountryCode != InputDeviceCountryCode::INVALID && mCountryCode != countryCode) { + ALOGW("InputDevice::configure(): %s device contains multiple unique country " + "codes", + getName().c_str()); + } + mCountryCode = countryCode; + } }); mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL); @@ -297,10 +320,13 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } } - if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { + if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) { + // Do not execute this code on the first configure, because 'setEnabled' would call + // InputMapper::reset, and you can't reset a mapper before it has been configured. + // The mappers are configured for the first time at the bottom of this function. auto it = config->disabledDevices.find(mId); bool enabled = it == config->disabledDevices.end(); - setEnabled(enabled, when); + out += setEnabled(enabled, when); } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { @@ -353,37 +379,42 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config // For first-time configuration, only allow device to be disabled after mappers have // finished configuring. This is because we need to read some of the properties from // the device's open fd. - setEnabled(enabled, when); + out += setEnabled(enabled, when); } } - for_each_mapper([this, when, config, changes](InputMapper& mapper) { - mapper.configure(when, config, changes); + for_each_mapper([this, when, &config, changes, &out](InputMapper& mapper) { + out += mapper.configure(when, config, changes); mSources |= mapper.getSources(); }); // If a device is just plugged but it might be disabled, we need to update some info like // axis range of touch from each InputMapper first, then disable it. if (!changes) { - setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); + out += setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), + when); } } + return out; } -void InputDevice::reset(nsecs_t when) { - for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); }); +std::list<NotifyArgs> InputDevice::reset(nsecs_t when) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.reset(when); }); mContext->updateGlobalMetaState(); - notifyReset(when); + out.push_back(notifyReset(when)); + return out; } -void InputDevice::process(const RawEvent* rawEvents, size_t count) { +std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) { // Process all of the events in order for each mapper. // We cannot simply ask each mapper to process them in bulk because mappers may // have side-effects that must be interleaved. For example, joystick movement events and // gamepad button presses are handled by different mappers but they should be dispatched // in the order received. + std::list<NotifyArgs> out; for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { if (DEBUG_RAW_EVENTS) { ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, @@ -405,28 +436,33 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); mDropUntilNextSync = true; - reset(rawEvent->when); + out += reset(rawEvent->when); } else { - for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) { - mapper.process(rawEvent); + for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) { + out += mapper.process(rawEvent); }); } --count; } + return out; } -void InputDevice::timeoutExpired(nsecs_t when) { - for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); }); +std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); }); + return out; } -void InputDevice::updateExternalStylusState(const StylusState& state) { - for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); }); +std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& state) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.updateExternalStylusState(state); }); + return out; } InputDeviceInfo InputDevice::getDeviceInfo() { InputDeviceInfo outDeviceInfo; outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic); + mHasMic, mCountryCode); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); }); @@ -470,12 +506,12 @@ int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc ge return result; } -bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) { bool result = false; - for_each_mapper([&result, sourceMask, numCodes, keyCodes, outFlags](InputMapper& mapper) { + for_each_mapper([&result, sourceMask, keyCodes, outFlags](InputMapper& mapper) { if (sourcesMatchMask(mapper.getSources(), sourceMask)) { - result |= mapper.markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + result |= mapper.markSupportedKeyCodes(sourceMask, keyCodes, outFlags); } }); return result; @@ -498,14 +534,17 @@ int32_t InputDevice::getKeyCodeForKeyLocation(int32_t locationKeyCode) const { return *result; } -void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) { - for_each_mapper([sequence, repeat, token](InputMapper& mapper) { - mapper.vibrate(sequence, repeat, token); - }); +std::list<NotifyArgs> InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, + int32_t token) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.vibrate(sequence, repeat, token); }); + return out; } -void InputDevice::cancelVibrate(int32_t token) { - for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); }); +std::list<NotifyArgs> InputDevice::cancelVibrate(int32_t token) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelVibrate(token); }); + return out; } bool InputDevice::isVibrating() { @@ -548,8 +587,10 @@ void InputDevice::flushSensor(InputDeviceSensorType sensorType) { for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); }); } -void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) { - for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); }); +std::list<NotifyArgs> InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; + for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelTouch(when, readTime); }); + return out; } bool InputDevice::setLightColor(int32_t lightId, int32_t color) { @@ -584,13 +625,18 @@ void InputDevice::updateMetaState(int32_t keyCode) { }); } +void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { + for_each_subdevice([fromKeyCode, toKeyCode](auto& context) { + context.addKeyRemapping(fromKeyCode, toKeyCode); + }); +} + void InputDevice::bumpGeneration() { mGeneration = mContext->bumpGeneration(); } -void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); - mContext->getListener().notifyDeviceReset(&args); +NotifyDeviceResetArgs InputDevice::notifyReset(nsecs_t when) { + return NotifyDeviceResetArgs(mContext->getNextId(), when, mId); } std::optional<int32_t> InputDevice::getAssociatedDisplayId() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 905b348caa..57f679c022 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -58,6 +58,17 @@ static bool isSubDevice(const InputDeviceIdentifier& identifier1, identifier1.location == identifier2.location); } +static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { + const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action); + if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER && + actionMasked != AMOTION_EVENT_ACTION_DOWN && + actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) { + return false; + } + const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action); + return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType); +} + // --- InputReader --- InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, @@ -101,8 +112,10 @@ status_t InputReader::stop() { void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; + // Copy some state so that we can access it outside the lock later. bool inputDevicesChanged = false; std::vector<InputDeviceInfo> inputDevices; + std::list<NotifyArgs> notifyArgs; { // acquire lock std::scoped_lock _l(mLock); @@ -120,14 +133,14 @@ void InputReader::loopOnce() { } } // release lock - size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis); { // acquire lock std::scoped_lock _l(mLock); mReaderIsAliveCondition.notify_all(); - if (count) { - processEventsLocked(mEventBuffer, count); + if (!events.empty()) { + notifyArgs += processEventsLocked(events.data(), events.size()); } if (mNextTimeout != LLONG_MAX) { @@ -137,7 +150,7 @@ void InputReader::loopOnce() { ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); } mNextTimeout = LLONG_MAX; - timeoutExpiredLocked(now); + notifyArgs += timeoutExpiredLocked(now); } } @@ -152,6 +165,16 @@ void InputReader::loopOnce() { mPolicy->notifyInputDevicesChanged(inputDevices); } + // Notify the policy of the start of every new stylus gesture outside the lock. + for (const auto& args : notifyArgs) { + const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args); + if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) { + mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime); + } + } + + notifyAll(std::move(notifyArgs)); + // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -162,7 +185,8 @@ void InputReader::loopOnce() { mQueuedListener.flush(); } -void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { +std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { + std::list<NotifyArgs> out; for (const RawEvent* rawEvent = rawEvents; count;) { int32_t type = rawEvent->type; size_t batchSize = 1; @@ -178,7 +202,7 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { if (DEBUG_RAW_EVENTS) { ALOGD("BatchSize: %zu Count: %zu", batchSize, count); } - processEventsForDeviceLocked(deviceId, rawEvent, batchSize); + out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: @@ -198,6 +222,7 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { count -= batchSize; rawEvent += batchSize; } + return out; } void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { @@ -208,8 +233,9 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId); std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier); - device->configure(when, &mConfig, 0); - device->reset(when); + + notifyAll(device->configure(when, &mConfig, 0)); + notifyAll(device->reset(when)); if (device->isIgnored()) { ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' " @@ -282,10 +308,12 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { notifyExternalStylusPresenceChangedLocked(); } + std::list<NotifyArgs> resetEvents; if (device->hasEventHubDevices()) { - device->configure(when, &mConfig, 0); + resetEvents += device->configure(when, &mConfig, 0); } - device->reset(when); + resetEvents += device->reset(when); + notifyAll(std::move(resetEvents)); } std::shared_ptr<InputDevice> InputReader::createDeviceLocked( @@ -308,21 +336,22 @@ std::shared_ptr<InputDevice> InputReader::createDeviceLocked( return device; } -void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, - size_t count) { +std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId, + const RawEvent* rawEvents, + size_t count) { auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { ALOGW("Discarding event for unknown eventHubId %d.", eventHubId); - return; + return {}; } std::shared_ptr<InputDevice>& device = deviceIt->second; if (device->isIgnored()) { // ALOGD("Discarding event for ignored deviceId %d.", deviceId); - return; + return {}; } - device->process(rawEvents, count); + return device->process(rawEvents, count); } InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const { @@ -336,13 +365,15 @@ InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const { return nullptr; } -void InputReader::timeoutExpiredLocked(nsecs_t when) { +std::list<NotifyArgs> InputReader::timeoutExpiredLocked(nsecs_t when) { + std::list<NotifyArgs> out; for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; if (!device->isIgnored()) { - device->timeoutExpired(when); + out += device->timeoutExpired(when); } } + return out; } int32_t InputReader::nextInputDeviceIdLocked() { @@ -377,7 +408,7 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { } else { for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; - device->configure(now, &mConfig, changes); + notifyAll(device->configure(now, &mConfig, changes)); } } @@ -394,6 +425,12 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { } } +void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) { + for (const NotifyArgs& args : argsList) { + mQueuedListener.notify(args); + } +} + void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; @@ -432,11 +469,13 @@ void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& o } } -void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) { +std::list<NotifyArgs> InputReader::dispatchExternalStylusStateLocked(const StylusState& state) { + std::list<NotifyArgs> out; for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; - device->updateExternalStylusState(state); + out += device->updateExternalStylusState(state); } + return out; } void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { @@ -583,34 +622,43 @@ void InputReader::toggleCapsLockState(int32_t deviceId) { device->updateMetaState(AKEYCODE_CAPS_LOCK); } -bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, + const std::vector<int32_t>& keyCodes, uint8_t* outFlags) { std::scoped_lock _l(mLock); - memset(outFlags, 0, numCodes); - return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags); + memset(outFlags, 0, keyCodes.size()); + return markSupportedKeyCodesLocked(deviceId, sourceMask, keyCodes, outFlags); } bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, + const std::vector<int32_t>& keyCodes, uint8_t* outFlags) { bool result = false; if (deviceId >= 0) { InputDevice* device = findInputDeviceLocked(deviceId); if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + result = device->markSupportedKeyCodes(sourceMask, keyCodes, outFlags); } } else { for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + result |= device->markSupportedKeyCodes(sourceMask, keyCodes, outFlags); } } } return result; } +void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device != nullptr) { + device->addKeyRemapping(fromKeyCode, toKeyCode); + } +} + int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { std::scoped_lock _l(mLock); @@ -642,7 +690,7 @@ void InputReader::vibrate(int32_t deviceId, const VibrationSequence& sequence, s InputDevice* device = findInputDeviceLocked(deviceId); if (device) { - device->vibrate(sequence, repeat, token); + notifyAll(device->vibrate(sequence, repeat, token)); } } @@ -651,7 +699,7 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { InputDevice* device = findInputDeviceLocked(deviceId); if (device) { - device->cancelVibrate(token); + notifyAll(device->cancelVibrate(token)); } } @@ -721,7 +769,10 @@ std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) { if (!eventHubId) return {}; const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); - if (batteryIds.empty()) return {}; + if (batteryIds.empty()) { + ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId); + return {}; + } return mEventHub->getBatteryCapacity(*eventHubId, batteryIds.front()); } @@ -741,10 +792,35 @@ std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) { if (!eventHubId) return {}; const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); - if (batteryIds.empty()) return {}; + if (batteryIds.empty()) { + ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId); + return {}; + } return mEventHub->getBatteryStatus(*eventHubId, batteryIds.front()); } +std::optional<std::string> InputReader::getBatteryDevicePath(int32_t deviceId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) return {}; + + std::optional<int32_t> eventHubId = device->getBatteryEventHubId(); + if (!eventHubId) return {}; + const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); + if (batteryIds.empty()) { + ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId); + return {}; + } + const auto batteryInfo = mEventHub->getRawBatteryInfo(*eventHubId, batteryIds.front()); + if (!batteryInfo) { + ALOGW("%s: Failed to get RawBatteryInfo for battery %d of EventHub device %d", __func__, + batteryIds.front(), *eventHubId); + return {}; + } + return batteryInfo->path; +} + std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) { std::scoped_lock _l(mLock); @@ -807,6 +883,16 @@ std::optional<int32_t> InputReader::getLightPlayerId(int32_t deviceId, int32_t l return std::nullopt; } +std::optional<std::string> InputReader::getBluetoothAddress(int32_t deviceId) const { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->getBluetoothAddress(); + } + return std::nullopt; +} + bool InputReader::isInputDeviceEnabled(int32_t deviceId) { std::scoped_lock _l(mLock); @@ -987,18 +1073,15 @@ void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceI mReader->getExternalStylusDevicesLocked(outDevices); } -void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { - mReader->dispatchExternalStylusStateLocked(state); +std::list<NotifyArgs> InputReader::ContextImpl::dispatchExternalStylusState( + const StylusState& state) { + return mReader->dispatchExternalStylusStateLocked(state); } InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } -InputListenerInterface& InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener; -} - EventHubInterface* InputReader::ContextImpl::getEventHub() { return mReader->mEventHub.get(); } diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h index d8376894f6..e107d882b7 100644 --- a/services/inputflinger/reader/Macros.h +++ b/services/inputflinger/reader/Macros.h @@ -14,33 +14,70 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_MACROS_H -#define _UI_INPUTREADER_MACROS_H +#pragma once #define LOG_TAG "InputReader" //#define LOG_NDEBUG 0 +#include <log/log.h> +#include <log/log_event_list.h> -// Log debug messages for each raw event received from the EventHub. -static constexpr bool DEBUG_RAW_EVENTS = false; +namespace android { +/** + * Log debug messages for each raw event received from the EventHub. + * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG" (requires restart) + */ +const bool DEBUG_RAW_EVENTS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO); -// Log debug messages about virtual key processing. -static constexpr bool DEBUG_VIRTUAL_KEYS = false; +/** + * Log debug messages about virtual key processing. + * Enable this via "adb shell setprop log.tag.InputReaderVirtualKeys DEBUG" (requires restart) + */ +const bool DEBUG_VIRTUAL_KEYS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VirtualKeys", ANDROID_LOG_INFO); -// Log debug messages about pointers. -static constexpr bool DEBUG_POINTERS = false; +/** + * Log debug messages about pointers. + * Enable this via "adb shell setprop log.tag.InputReaderPointers DEBUG" (requires restart) + */ +const bool DEBUG_POINTERS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Pointers", ANDROID_LOG_INFO); -// Log debug messages about pointer assignment calculations. -static constexpr bool DEBUG_POINTER_ASSIGNMENT = false; +/** + * Log debug messages about pointer assignment calculations. + * Enable this via "adb shell setprop log.tag.InputReaderPointerAssignment DEBUG" (requires restart) + */ +const bool DEBUG_POINTER_ASSIGNMENT = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "PointerAssignment", ANDROID_LOG_INFO); +/** + * Log debug messages about gesture detection. + * Enable this via "adb shell setprop log.tag.InputReaderGestures DEBUG" (requires restart) + */ +const bool DEBUG_GESTURES = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Gestures", ANDROID_LOG_INFO); -// Log debug messages about gesture detection. -static constexpr bool DEBUG_GESTURES = false; +/** + * Log debug messages about the vibrator. + * Enable this via "adb shell setprop log.tag.InputReaderVibrator DEBUG" (requires restart) + */ +const bool DEBUG_VIBRATOR = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Vibrator", ANDROID_LOG_INFO); -// Log debug messages about the vibrator. -static constexpr bool DEBUG_VIBRATOR = false; +/** + * Log debug messages about fusing stylus data. + * Enable this via "adb shell setprop log.tag.InputReaderStylusFusion DEBUG" (requires restart) + */ +const bool DEBUG_STYLUS_FUSION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "StylusFusion", ANDROID_LOG_INFO); -// Log debug messages about fusing stylus data. -static constexpr bool DEBUG_STYLUS_FUSION = false; +/** + * Log detailed debug messages about input device lights. + * Enable this via "adb shell setprop log.tag.InputReaderLightDetails DEBUG" (requires restart) + */ +const bool DEBUG_LIGHT_DETAILS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LightDetails", ANDROID_LOG_INFO); +} // namespace android #define INDENT " " #define INDENT2 " " @@ -77,5 +114,3 @@ static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { } } // namespace android - -#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp index 2f8138b832..627dcba9cf 100644 --- a/services/inputflinger/reader/TouchVideoDevice.cpp +++ b/services/inputflinger/reader/TouchVideoDevice.cpp @@ -198,8 +198,9 @@ std::optional<TouchVideoFrame> TouchVideoDevice::readFrame() { if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) { // We use CLOCK_MONOTONIC for input events, so if the clocks don't match, // we can't compare timestamps. Just log a warning, since this is a driver issue - ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec, - buf.timestamp.tv_usec); + ALOGW("The timestamp %lld.%lld was not acquired using CLOCK_MONOTONIC", + static_cast<long long>(buf.timestamp.tv_sec), + static_cast<long long>(buf.timestamp.tv_usec)); } std::vector<int16_t> data(mHeight * mWidth); const int16_t* readFrom = mReadLocations[buf.index]; diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 8065f57524..cedbacb046 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -16,15 +16,13 @@ #include <locale> #include <regex> +#include <set> #include <ftl/enum.h> #include "../Macros.h" #include "PeripheralController.h" -// Log detailed debug messages about input device lights. -static constexpr bool DEBUG_LIGHT_DETAILS = false; - namespace android { static inline int32_t getAlpha(int32_t color) { @@ -73,7 +71,7 @@ std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(i // If the light node doesn't have max brightness, use the default max brightness. int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS); - float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness; // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255] if (rawMaxBrightness != MAX_BRIGHTNESS) { brightness = brightness * ratio; @@ -92,7 +90,7 @@ void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int3 } // If the light node doesn't have max brightness, use the default max brightness. int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS); - float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness; // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness] if (rawMaxBrightness != MAX_BRIGHTNESS) { brightness = ceil(brightness / ratio); @@ -274,7 +272,8 @@ void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) { for (const auto& [lightId, light] : mLights) { // Input device light doesn't support ordinal, always pass 1. - InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */); + InputDeviceLightInfo lightInfo(light->name, light->id, light->type, light->capabilityFlags, + 1 /* ordinal */); deviceInfo->addLightInfo(lightInfo); } } @@ -287,6 +286,8 @@ void PeripheralController::dump(std::string& dump) { dump += StringPrintf(INDENT4 "Id: %d", lightId); dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str()); dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str()); + dump += StringPrintf(INDENT4 "Capability flags: %s", + light->capabilityFlags.string().c_str()); light->dump(dump); } } @@ -366,6 +367,8 @@ void PeripheralController::configureLights() { std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds; // Map from player Id to raw light Id std::unordered_map<int32_t, int32_t> playerIdLightIds; + // Set of Keyboard backlights + std::set<int32_t> keyboardBacklightIds; // Check raw lights const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds(); @@ -394,6 +397,10 @@ void PeripheralController::configureLights() { } } } + // Check if this is a Keyboard backlight + if (rawInfo->flags.test(InputLightClass::KEYBOARD_BACKLIGHT)) { + keyboardBacklightIds.insert(rawId); + } // Check if this is an LED of RGB light if (rawInfo->flags.test(InputLightClass::RED)) { hasRedLed = true; @@ -434,8 +441,21 @@ void PeripheralController::configureLights() { ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED), rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); } + bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) != + keyboardBacklightIds.end() && + keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) != + keyboardBacklightIds.end() && + keyboardBacklightIds.find(rawRgbIds.at(LightColor::BLUE)) != + keyboardBacklightIds.end() && + (!rawGlobalId.has_value() || + keyboardBacklightIds.find(rawGlobalId.value()) != keyboardBacklightIds.end()); + std::unique_ptr<Light> light = - std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId); + std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, + isKeyboardBacklight + ? InputDeviceLightType::KEYBOARD_BACKLIGHT + : InputDeviceLightType::INPUT, + rawRgbIds, rawGlobalId); mLights.insert_or_assign(light->id, std::move(light)); // Remove from raw light info as they've been composed a RBG light. rawInfos.erase(rawRgbIds.at(LightColor::RED)); @@ -448,6 +468,10 @@ void PeripheralController::configureLights() { // Check the rest of raw light infos for (const auto& [rawId, rawInfo] : rawInfos) { + InputDeviceLightType type = keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end() + ? InputDeviceLightType::KEYBOARD_BACKLIGHT + : InputDeviceLightType::INPUT; + // If the node is multi-color led, construct a MULTI_COLOR light if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) && rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) { @@ -456,7 +480,7 @@ void PeripheralController::configureLights() { } std::unique_ptr<Light> light = std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId, - rawInfo.id); + type, rawInfo.id); mLights.insert_or_assign(light->id, std::move(light)); continue; } @@ -465,7 +489,7 @@ void PeripheralController::configureLights() { ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); } std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name, - ++mNextId, rawInfo.id); + ++mNextId, type, rawInfo.id); mLights.insert_or_assign(light->id, std::move(light)); } diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h index ac951ebe2a..8ac42c3792 100644 --- a/services/inputflinger/reader/controller/PeripheralController.h +++ b/services/inputflinger/reader/controller/PeripheralController.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H -#define _UI_INPUTREADER_LIGHT_CONTROLLER_H +#pragma once #include "PeripheralControllerInterface.h" @@ -68,6 +67,7 @@ private: std::string name; int32_t id; InputDeviceLightType type; + ftl::Flags<InputDeviceLightCapability> capabilityFlags; virtual bool setLightColor(int32_t color) { return false; } virtual std::optional<int32_t> getLightColor() { return std::nullopt; } @@ -82,8 +82,10 @@ private: struct MonoLight : public Light { explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id, - int32_t rawId) - : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {} + InputDeviceLightType type, int32_t rawId) + : Light(context, name, id, type), rawId(rawId) { + capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS; + } int32_t rawId; bool setLightColor(int32_t color) override; @@ -92,15 +94,15 @@ private: }; struct RgbLight : public Light { - explicit RgbLight(InputDeviceContext& context, int32_t id, + explicit RgbLight(InputDeviceContext& context, int32_t id, InputDeviceLightType type, const std::unordered_map<LightColor, int32_t>& rawRgbIds, std::optional<int32_t> rawGlobalId) - : Light(context, "RGB", id, InputDeviceLightType::RGB), - rawRgbIds(rawRgbIds), - rawGlobalId(rawGlobalId) { + : Light(context, "RGB", id, type), rawRgbIds(rawRgbIds), rawGlobalId(rawGlobalId) { brightness = rawGlobalId.has_value() ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS) : MAX_BRIGHTNESS; + capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS; + capabilityFlags |= InputDeviceLightCapability::RGB; } // Map from color to raw light id. std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds; @@ -115,8 +117,11 @@ private: struct MultiColorLight : public Light { explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id, - int32_t rawId) - : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {} + InputDeviceLightType type, int32_t rawId) + : Light(context, name, id, type), rawId(rawId) { + capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS; + capabilityFlags |= InputDeviceLightCapability::RGB; + } int32_t rawId; bool setLightColor(int32_t color) override; @@ -132,7 +137,7 @@ private: // Map from player Id to raw light Id std::unordered_map<int32_t, int32_t> rawLightIds; - bool setLightPlayerId(int32_t palyerId) override; + bool setLightPlayerId(int32_t playerId) override; std::optional<int32_t> getLightPlayerId() override; void dump(std::string& dump) override; }; @@ -150,5 +155,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h index 306e36119b..76ed1ca038 100644 --- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h +++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H -#define _UI_INPUTREADER_INPUT_CONTROLLER_H +#pragma once #include "EventHub.h" #include "InputDevice.h" @@ -50,5 +49,3 @@ public: }; } // namespace android - -#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 54c6810a33..8a844b2088 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef _RUNTIME_EVENT_HUB_H -#define _RUNTIME_EVENT_HUB_H +#pragma once #include <bitset> #include <climits> #include <filesystem> #include <unordered_map> +#include <utility> #include <vector> #include <batteryservice/BatteryService.h> @@ -36,13 +36,15 @@ #include <sys/epoll.h> #include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/KeyedVector.h> #include <utils/List.h> #include <utils/Log.h> #include <utils/Mutex.h> #include "TouchVideoDevice.h" #include "VibrationElement.h" +#include "android/hardware/input/InputDeviceCountryCode.h" + +struct inotify_event; namespace android { @@ -65,22 +67,15 @@ struct RawEvent { /* Describes an absolute axis. */ struct RawAbsoluteAxisInfo { - bool valid; // true if the information is valid, false otherwise - - int32_t minValue; // minimum value - int32_t maxValue; // maximum value - int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 - int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - int32_t resolution; // resolution in units per mm or radians per mm - - inline void clear() { - valid = false; - minValue = 0; - maxValue = 0; - flat = 0; - fuzz = 0; - resolution = 0; - } + bool valid{false}; // true if the information is valid, false otherwise + + int32_t minValue{}; // minimum value + int32_t maxValue{}; // maximum value + int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8 + int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise + int32_t resolution{}; // resolution in units per mm or radians per mm + + inline void clear() { *this = RawAbsoluteAxisInfo(); } }; /* @@ -99,7 +94,7 @@ enum class InputDeviceClass : uint32_t { /* The input device is a cursor device such as a trackball or mouse. */ CURSOR = 0x00000008, - /* The input device is a multi-touch touchscreen. */ + /* The input device is a multi-touch touchscreen or touchpad. */ TOUCH_MT = 0x00000010, /* The input device is a directional pad (implies keyboard, has DPAD keys). */ @@ -135,6 +130,9 @@ enum class InputDeviceClass : uint32_t { /* The input device has sysfs controllable lights */ LIGHT = 0x00008000, + /* The input device is a touchpad, requiring an on-screen cursor. */ + TOUCHPAD = 0x00010000, + /* The input device is virtual (not a real device, not part of UI configuration). */ VIRTUAL = 0x40000000, @@ -172,6 +170,8 @@ enum class InputLightClass : uint32_t { MULTI_INTENSITY = 0x00000040, /* The input light has max brightness node. */ MAX_BRIGHTNESS = 0x00000080, + /* The input light has kbd_backlight name */ + KEYBOARD_BACKLIGHT = 0x00000100, }; enum class InputBatteryClass : uint32_t { @@ -191,6 +191,9 @@ struct RawLightInfo { ftl::Flags<InputLightClass> flags; std::array<int32_t, COLOR_NUM> rgbIndex; std::filesystem::path path; + + bool operator==(const RawLightInfo&) const = default; + bool operator!=(const RawLightInfo&) const = default; }; /* Describes a raw battery. */ @@ -199,6 +202,9 @@ struct RawBatteryInfo { std::string name; ftl::Flags<InputBatteryClass> flags; std::filesystem::path path; + + bool operator==(const RawBatteryInfo&) const = default; + bool operator!=(const RawBatteryInfo&) const = default; }; /* @@ -256,6 +262,9 @@ public: virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0; + virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, + int32_t toKeyCode) const = 0; + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const = 0; @@ -278,29 +287,30 @@ public: * * Returns the number of events obtained, or 0 if the timeout expired. */ - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0; + virtual std::vector<RawEvent> getEvents(int timeoutMillis) = 0; virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0; - virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId, - int32_t absCode) = 0; + virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor( + int32_t deviceId, int32_t absCode) const = 0; // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node, // containing the raw info of the sysfs node structure. - virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0; + virtual std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const = 0; virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, - int32_t BatteryId) = 0; + int32_t BatteryId) const = 0; // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node, // containing the raw info of the sysfs node structure. - virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0; - virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) = 0; - virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) = 0; + virtual std::vector<int32_t> getRawLightIds(int32_t deviceId) const = 0; + virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, + int32_t lightId) const = 0; + virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const = 0; virtual void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) = 0; virtual std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( - int32_t deviceId, int32_t lightId) = 0; + int32_t deviceId, int32_t lightId) const = 0; virtual void setLightIntensities(int32_t deviceId, int32_t lightId, std::unordered_map<LightColor, int32_t> intensities) = 0; - /* - * Query current input state. - */ + /* Query Country code associated with the input device. */ + virtual hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const = 0; + /* Query current input state. */ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; @@ -311,7 +321,7 @@ public: /* * Examine key input devices for specific framework keycode support */ - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + virtual bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const = 0; virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; @@ -331,7 +341,7 @@ public: /* Control the vibrator. */ virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0; virtual void cancelVibrate(int32_t deviceId) = 0; - virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0; + virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) const = 0; /* Query battery level. */ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId, @@ -347,13 +357,13 @@ public: virtual void wake() = 0; /* Dump EventHub state to a string. */ - virtual void dump(std::string& dump) = 0; + virtual void dump(std::string& dump) const = 0; /* Called by the heatbeat to ensures that the reader has not deadlocked. */ - virtual void monitor() = 0; + virtual void monitor() const = 0; /* Return true if the device is enabled. */ - virtual bool isDeviceEnabled(int32_t deviceId) = 0; + virtual bool isDeviceEnabled(int32_t deviceId) const = 0; /* Enable an input device */ virtual status_t enableDevice(int32_t deviceId) = 0; @@ -453,6 +463,9 @@ public: bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; + void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, + int32_t toKeyCode) const override final; + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override final; @@ -461,23 +474,27 @@ public: AxisInfo* outAxisInfo) const override final; base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor( - int32_t deviceId, int32_t absCode) override final; + int32_t deviceId, int32_t absCode) const override final; - const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final; + std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override final; std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, - int32_t BatteryId) override final; + int32_t BatteryId) const override final; - const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final; + std::vector<int32_t> getRawLightIds(int32_t deviceId) const override final; - std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final; + std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, + int32_t lightId) const override final; - std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override final; + std::optional<int32_t> getLightBrightness(int32_t deviceId, + int32_t lightId) const override final; void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override final; std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( - int32_t deviceId, int32_t lightId) override final; + int32_t deviceId, int32_t lightId) const override final; void setLightIntensities(int32_t deviceId, int32_t lightId, std::unordered_map<LightColor, int32_t> intensities) override final; + hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const override final; + void setExcludedDevices(const std::vector<std::string>& devices) override final; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; @@ -488,10 +505,10 @@ public: status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override final; - bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const override final; - size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final; + std::vector<RawEvent> getEvents(int timeoutMillis) override final; std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final; bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final; @@ -510,15 +527,15 @@ public: void vibrate(int32_t deviceId, const VibrationElement& effect) override final; void cancelVibrate(int32_t deviceId) override final; - std::vector<int32_t> getVibratorIds(int32_t deviceId) override final; + std::vector<int32_t> getVibratorIds(int32_t deviceId) const override final; void requestReopenDevices() override final; void wake() override final; - void dump(std::string& dump) override final; + void dump(std::string& dump) const override final; - void monitor() override final; + void monitor() const override final; std::optional<int32_t> getBatteryCapacity(int32_t deviceId, int32_t batteryId) const override final; @@ -526,7 +543,7 @@ public: std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const override final; - bool isDeviceEnabled(int32_t deviceId) override final; + bool isDeviceEnabled(int32_t deviceId) const override final; status_t enableDevice(int32_t deviceId) override final; @@ -535,20 +552,17 @@ public: ~EventHub() override; private: + // Holds information about the sysfs device associated with the Device. struct AssociatedDevice { - // The device descriptor from evdev device the misc device associated with. - std::string descriptor; // The sysfs root path of the misc device. std::filesystem::path sysfsRootPath; + hardware::input::InputDeviceCountryCode countryCode; + std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos; + std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos; - int32_t nextBatteryId; - int32_t nextLightId; - std::unordered_map<int32_t, RawBatteryInfo> batteryInfos; - std::unordered_map<int32_t, RawLightInfo> lightInfos; - explicit AssociatedDevice(std::filesystem::path sysfsRootPath) - : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {} - bool configureBatteryLocked(); - bool configureLightsLocked(); + bool operator==(const AssociatedDevice&) const = default; + bool operator!=(const AssociatedDevice&) const = default; + std::string dump() const; }; struct Device { @@ -581,13 +595,13 @@ private: int16_t ffEffectId; // initially -1 // A shared_ptr of a device associated with the input device. - // The input devices with same descriptor has the same associated device. - std::shared_ptr<AssociatedDevice> associatedDevice; + // The input devices that have the same sysfs path have the same associated device. + std::shared_ptr<const AssociatedDevice> associatedDevice; int32_t controllerNumber; - Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier); + Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier, + std::shared_ptr<const AssociatedDevice> assocDev); ~Device(); void close(); @@ -632,6 +646,8 @@ private: void createVirtualKeyboardLocked() REQUIRES(mLock); void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock); void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock); + std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked( + const std::filesystem::path& devicePath) const REQUIRES(mLock); void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock); void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock); @@ -648,7 +664,8 @@ private: status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock); status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock); void scanDevicesLocked() REQUIRES(mLock); - status_t readNotifyLocked() REQUIRES(mLock); + base::Result<void> readNotifyLocked() REQUIRES(mLock); + void handleNotifyEventLocked(const inotify_event&) REQUIRES(mLock); Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock); Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock); @@ -728,5 +745,3 @@ private: }; } // namespace android - -#endif // _RUNTIME_EVENT_HUB_H diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index b3a24af8a9..6fa21e5362 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_INPUT_DEVICE_H -#define _UI_INPUTREADER_INPUT_DEVICE_H +#pragma once #include <ftl/flags.h> #include <input/DisplayViewport.h> @@ -30,6 +29,7 @@ #include "EventHub.h" #include "InputReaderBase.h" #include "InputReaderContext.h" +#include "NotifyArgs.h" namespace android { @@ -51,6 +51,9 @@ public: inline int32_t getGeneration() const { return mGeneration; } inline const std::string getName() const { return mIdentifier.name; } inline const std::string getDescriptor() { return mIdentifier.descriptor; } + inline std::optional<std::string> getBluetoothAddress() const { + return mIdentifier.bluetoothAddress; + } inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; } inline uint32_t getSources() const { return mSources; } inline bool hasEventHubDevices() const { return !mDevices.empty(); } @@ -70,29 +73,32 @@ public: inline bool isIgnored() { return !getMapperCount(); } bool isEnabled(); - void setEnabled(bool enabled, nsecs_t when); + [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when); void dump(std::string& dump, const std::string& eventHubDevStr); void addEventHubDevice(int32_t eventHubId, bool populateMappers = true); void removeEventHubDevice(int32_t eventHubId); - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - void reset(nsecs_t when); - void process(const RawEvent* rawEvents, size_t count); - void timeoutExpired(nsecs_t when); - void updateExternalStylusState(const StylusState& state); + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes); + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when); + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count); + [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when); + [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(const StylusState& state); InputDeviceInfo getDeviceInfo(); int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const; - bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags); - void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token); - void cancelVibrate(int32_t token); + [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat, + int32_t token); + [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token); bool isVibrating(); std::vector<int32_t> getVibratorIds(); - void cancelTouch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime); bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, std::chrono::microseconds maxBatchReportLatency); void disableSensor(InputDeviceSensorType sensorType); @@ -108,9 +114,11 @@ public: int32_t getMetaState(); void updateMetaState(int32_t keyCode); + void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + void bumpGeneration(); - void notifyReset(nsecs_t when); + [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when); inline const PropertyMap& getConfiguration() { return mConfiguration; } inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } @@ -155,6 +163,7 @@ private: int32_t mId; int32_t mGeneration; int32_t mControllerNumber; + hardware::input::InputDeviceCountryCode mCountryCode; InputDeviceIdentifier mIdentifier; std::string mAlias; ftl::Flags<InputDeviceClass> mClasses; @@ -271,6 +280,10 @@ public: inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); } + inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const { + mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode); + } + inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState, @@ -308,6 +321,9 @@ public: } inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); } + inline hardware::input::InputDeviceCountryCode getCountryCode() const { + return mEventHub->getCountryCode(mId); + } inline int32_t getScanCodeState(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode); } @@ -321,9 +337,9 @@ public: inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const { return mEventHub->getAbsoluteAxisValue(mId, code, outValue); } - inline bool markSupportedKeyCodes(size_t numCodes, const int32_t* keyCodes, + inline bool markSupportedKeyCodes(const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const { - return mEventHub->markSupportedKeyCodes(mId, numCodes, keyCodes, outFlags); + return mEventHub->markSupportedKeyCodes(mId, keyCodes, outFlags); } inline bool hasScanCode(int32_t scanCode) const { return mEventHub->hasScanCode(mId, scanCode); @@ -368,8 +384,11 @@ public: mEventHub->getAbsoluteAxisInfo(mId, code, &info); return info.valid; } - inline bool isKeyPressed(int32_t code) const { - return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + inline bool isKeyPressed(int32_t scanCode) const { + return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN; + } + inline bool isKeyCodePressed(int32_t keyCode) const { + return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN; } inline int32_t getAbsoluteAxisValue(int32_t code) const { int32_t value; @@ -392,7 +411,9 @@ public: inline std::optional<DisplayViewport> getAssociatedViewport() const { return mDevice.getAssociatedViewport(); } - inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); } + [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) { + return mDevice.cancelTouch(when, readTime); + } inline void bumpGeneration() { mDevice.bumpGeneration(); } inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); } @@ -405,5 +426,3 @@ private: }; } // namespace android - -#endif //_UI_INPUTREADER_INPUT_DEVICE_H diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index daeaa1dbe3..e9c989a224 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_INPUT_READER_H -#define _UI_INPUTREADER_INPUT_READER_H +#pragma once #include <PointerControllerInterface.h> #include <android-base/thread_annotations.h> @@ -69,11 +68,13 @@ public: int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; + void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override; + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; void toggleCapsLockState(int32_t deviceId) override; - bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) override; void requestRefreshConfiguration(uint32_t changes) override; @@ -100,6 +101,8 @@ public: std::optional<int32_t> getBatteryStatus(int32_t deviceId) override; + std::optional<std::string> getBatteryDevicePath(int32_t deviceId) override; + std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) override; std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override; @@ -112,6 +115,8 @@ public: std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override; + std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override; + protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId, @@ -141,10 +146,9 @@ protected: int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override; void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mReader->mLock) override; - void dispatchExternalStylusState(const StylusState& outState) + [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState) REQUIRES(mReader->mLock) override; InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override; - InputListenerInterface& getListener() REQUIRES(mReader->mLock) override; EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override; int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override; @@ -169,10 +173,6 @@ private: InputReaderConfiguration mConfig GUARDED_BY(mLock); - // The event queue. - static const int EVENT_BUFFER_SIZE = 256; - RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock); - // An input device can represent a collection of EventHub devices. This map provides a way // to lookup the input device instance from the EventHub device id. std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices @@ -184,13 +184,15 @@ private: mDeviceToEventHubIdsMap GUARDED_BY(mLock); // low-level input event decoding and device management - void processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); + [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) + REQUIRES(mLock); void addDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock); void removeDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock); - void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count) - REQUIRES(mLock); - void timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock); + [[nodiscard]] std::list<NotifyArgs> processEventsForDeviceLocked(int32_t eventHubId, + const RawEvent* rawEvents, + size_t count) REQUIRES(mLock); + [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock); void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock); @@ -204,7 +206,8 @@ private: void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock); void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock); - void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock); + [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusStateLocked(const StylusState& state) + REQUIRES(mLock); // The PointerController that is shared among all the input devices that need it. std::weak_ptr<PointerControllerInterface> mPointerController; @@ -231,19 +234,20 @@ private: uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock); void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock); + void notifyAll(std::list<NotifyArgs>&& argsList); + PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock); // state queries typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) REQUIRES(mLock); - bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) REQUIRES(mLock); + bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, + const std::vector<int32_t>& keyCodes, uint8_t* outFlags) + REQUIRES(mLock); // find an InputDevice from an InputDevice id InputDevice* findInputDeviceLocked(int32_t deviceId) const REQUIRES(mLock); }; } // namespace android - -#endif // _UI_INPUTREADER_INPUT_READER_H diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 823d160f86..0beace19ab 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H -#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H +#pragma once #include <input/InputDevice.h> +#include "NotifyArgs.h" #include <vector> @@ -52,10 +52,10 @@ public: virtual int32_t bumpGeneration() = 0; virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0; - virtual void dispatchExternalStylusState(const StylusState& outState) = 0; + [[nodiscard]] virtual std::list<NotifyArgs> dispatchExternalStylusState( + const StylusState& outState) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface& getListener() = 0; virtual EventHubInterface* getEventHub() = 0; virtual int32_t getNextId() = 0; @@ -65,5 +65,3 @@ public: }; } // namespace android - -#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h index 17f158c9e1..ff15e0c660 100644 --- a/services/inputflinger/reader/include/StylusState.h +++ b/services/inputflinger/reader/include/StylusState.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_STYLUS_STATE_H -#define _UI_INPUTREADER_STYLUS_STATE_H +#pragma once #include <input/Input.h> @@ -25,29 +24,19 @@ namespace android { struct StylusState { /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; + nsecs_t when{}; + /* + * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0]. + * The presence of a pressure value indicates that the stylus is able to tell whether it is + * touching the display. + */ + std::optional<float> pressure{}; /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; + uint32_t buttons{}; /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; + int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN}; - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } + void clear() { *this = StylusState{}; } }; } // namespace android - -#endif // _UI_INPUTREADER_STYLUS_STATE_H diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h index 7de9b830b2..08eba31199 100644 --- a/services/inputflinger/reader/include/TouchVideoDevice.h +++ b/services/inputflinger/reader/include/TouchVideoDevice.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H -#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#pragma once #include <android-base/unique_fd.h> #include <input/TouchVideoFrame.h> @@ -123,5 +122,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index f4f3ae95a1..13e4d0cfbe 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -67,7 +67,7 @@ void CursorMotionAccumulator::finishSync() { // --- CursorInputMapper --- CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) - : InputMapper(deviceContext) {} + : InputMapper(deviceContext), mLastEventTime(std::numeric_limits<nsecs_t>::min()) {} CursorInputMapper::~CursorInputMapper() { if (mPointerController != nullptr) { @@ -117,6 +117,10 @@ void CursorInputMapper::dump(std::string& dump) { toString(mCursorScrollAccumulator.haveRelativeVWheel())); dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "WheelYVelocityControlParameters: %s", + mWheelYVelocityControl.getParameters().dump().c_str()); + dump += StringPrintf(INDENT3 "WheelXVelocityControlParameters: %s", + mWheelXVelocityControl.getParameters().dump().c_str()); dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str()); @@ -126,9 +130,10 @@ void CursorInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); } -void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> CursorInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); if (!changes) { // first time only mCursorScrollAccumulator.configure(getDeviceContext()); @@ -187,8 +192,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } bumpGeneration(); if (changes) { - NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener().notifyDeviceReset(&args); + out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); } } @@ -223,7 +227,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mDisplayId = mPointerController->getDisplayId(); } - mOrientation = DISPLAY_ORIENTATION_0; + mOrientation = ui::ROTATION_0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); // InputReader works in the un-rotated display coordinate space, so we don't need to do @@ -241,22 +245,22 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* bumpGeneration(); } + return out; } void CursorInputMapper::configureParameters() { mParameters.mode = Parameters::Mode::POINTER; - String8 cursorModeString; - if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"), - cursorModeString)) { + std::string cursorModeString; + if (getDeviceContext().getConfiguration().tryGetProperty("cursor.mode", cursorModeString)) { if (cursorModeString == "navigation") { mParameters.mode = Parameters::Mode::NAVIGATION; } else if (cursorModeString != "pointer" && cursorModeString != "default") { - ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); + ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str()); } } mParameters.orientationAware = false; - getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware", mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; @@ -273,9 +277,10 @@ void CursorInputMapper::dumpParameters(std::string& dump) { dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); } -void CursorInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) { mButtonState = 0; mDownTime = 0; + mLastEventTime = std::numeric_limits<nsecs_t>::min(); mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); @@ -285,23 +290,31 @@ void CursorInputMapper::reset(nsecs_t when) { mCursorMotionAccumulator.reset(getDeviceContext()); mCursorScrollAccumulator.reset(getDeviceContext()); - InputMapper::reset(when); + return InputMapper::reset(when); } -void CursorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; mCursorButtonAccumulator.process(rawEvent); mCursorMotionAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when, rawEvent->readTime); + const auto [eventTime, readTime] = + applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), + rawEvent->when, rawEvent->readTime, + mLastEventTime); + out += sync(eventTime, readTime); + mLastEventTime = eventTime; } + return out; } -void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { +std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; if (!mDisplayId) { // Ignore events when there is no target display configured. - return; + return out; } int32_t lastButtonState = mButtonState; @@ -392,8 +405,9 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } // Synthesize key down from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(), - mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState); + out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(), + mSource, *mDisplayId, policyFlags, lastButtonState, + currentButtonState); // Send motion event. if (downChanged || moved || scrolled || buttonsChanged) { @@ -413,40 +427,38 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime, - getDeviceId(), mSource, *mDisplayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&releaseArgs); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, + getDeviceId(), mSource, *mDisplayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {})); } } - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, *mDisplayId, policyFlags, motionEventAction, 0, 0, + metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, + /* videoFrames */ {})); if (buttonsPressed) { BitSet32 pressed(buttonsPressed); while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, *mDisplayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&pressArgs); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, + getDeviceId(), mSource, *mDisplayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {})); } } @@ -454,14 +466,14 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, *mDisplayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener().notifyMotion(&hoverArgs); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, *mDisplayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {})); } // Send scroll events. @@ -469,23 +481,25 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, *mDisplayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener().notifyMotion(&scrollArgs); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, *mDisplayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {})); } } // Synthesize key up from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource, - *mDisplayId, policyFlags, lastButtonState, currentButtonState); + out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), + mSource, *mDisplayId, policyFlags, lastButtonState, + currentButtonState); mCursorMotionAccumulator.finishSync(); mCursorScrollAccumulator.finishSync(); + return out; } int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 60b3dd9ee0..939cceb6b0 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H -#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H +#pragma once #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" @@ -23,6 +22,7 @@ #include <PointerControllerInterface.h> #include <input/VelocityControl.h> +#include <ui/Rotation.h> namespace android { @@ -59,10 +59,11 @@ public: virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) override; - virtual void reset(nsecs_t when) override; - virtual void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; @@ -115,19 +116,18 @@ private: // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. // std::nullopt), all events will be ignored. std::optional<int32_t> mDisplayId; - int32_t mOrientation; + ui::Rotation mOrientation; std::shared_ptr<PointerControllerInterface> mPointerController; int32_t mButtonState; nsecs_t mDownTime; + nsecs_t mLastEventTime; void configureParameters(); void dumpParameters(std::string& dump); - void sync(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime); }; } // namespace android - -#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 6b5d37f8d5..2809939c86 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -24,7 +24,7 @@ namespace android { ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext) - : InputMapper(deviceContext) {} + : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {} uint32_t ExternalStylusInputMapper::getSources() const { return AINPUT_SOURCE_STYLUS; @@ -32,8 +32,10 @@ uint32_t ExternalStylusInputMapper::getSources() const { void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f); + if (mRawPressureAxis.valid) { + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f); + } } void ExternalStylusInputMapper::dump(std::string& dump) { @@ -44,28 +46,32 @@ void ExternalStylusInputMapper::dump(std::string& dump) { dumpStylusState(dump, mStylusState); } -void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { +std::list<NotifyArgs> ExternalStylusInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDeviceContext()); + mTouchButtonAccumulator.configure(); + return {}; } -void ExternalStylusInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) { mSingleTouchMotionAccumulator.reset(getDeviceContext()); - mTouchButtonAccumulator.reset(getDeviceContext()); - InputMapper::reset(when); + mTouchButtonAccumulator.reset(); + return InputMapper::reset(when); } -void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; mSingleTouchMotionAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); + out += sync(rawEvent->when); } + return out; } -void ExternalStylusInputMapper::sync(nsecs_t when) { +std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.clear(); mStylusState.when = when; @@ -75,18 +81,17 @@ void ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; } - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; + auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure()); + mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) / + static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue); + } else if (mTouchButtonAccumulator.hasButtonTouch()) { + mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f; } mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - getContext()->dispatchExternalStylusState(mStylusState); + return getContext()->dispatchExternalStylusState(mStylusState); } } // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 516aa51a14..b6c9055cb7 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H -#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H +#pragma once #include "InputMapper.h" @@ -30,13 +29,14 @@ public: explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext); virtual ~ExternalStylusInputMapper() = default; - virtual uint32_t getSources() const override; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; - virtual void dump(std::string& dump) override; - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) override; - virtual void reset(nsecs_t when) override; - virtual void process(const RawEvent* rawEvent) override; + uint32_t getSources() const override; + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + void dump(std::string& dump) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; @@ -45,9 +45,7 @@ private: StylusState mStylusState; - void sync(nsecs_t when); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when); }; } // namespace android - -#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 7b185e029c..8e3539c3a6 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -19,6 +19,7 @@ #include "InputMapper.h" #include "InputDevice.h" +#include "input/PrintTools.h" namespace android { @@ -32,12 +33,18 @@ void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { void InputMapper::dump(std::string& dump) {} -void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) {} +std::list<NotifyArgs> InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + return {}; +} -void InputMapper::reset(nsecs_t when) {} +std::list<NotifyArgs> InputMapper::reset(nsecs_t when) { + return {}; +} -void InputMapper::timeoutExpired(nsecs_t when) {} +std::list<NotifyArgs> InputMapper::timeoutExpired(nsecs_t when) { + return {}; +} int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { return AKEY_STATE_UNKNOWN; @@ -55,14 +62,19 @@ int32_t InputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) const { return AKEYCODE_UNKNOWN; } -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) { return false; } -void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {} +std::list<NotifyArgs> InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, + int32_t token) { + return {}; +} -void InputMapper::cancelVibrate(int32_t token) {} +std::list<NotifyArgs> InputMapper::cancelVibrate(int32_t token) { + return {}; +} bool InputMapper::isVibrating() { return false; @@ -72,7 +84,9 @@ std::vector<int32_t> InputMapper::getVibratorIds() { return {}; } -void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {} +std::list<NotifyArgs> InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) { + return {}; +} bool InputMapper::enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, @@ -92,7 +106,9 @@ bool InputMapper::updateMetaState(int32_t keyCode) { return false; } -void InputMapper::updateExternalStylusState(const StylusState& state) {} +std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) { + return {}; +} status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo); @@ -114,7 +130,7 @@ void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAx void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str()); dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index fce6409b3f..104305b730 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_INPUT_MAPPER_H -#define _UI_INPUTREADER_INPUT_MAPPER_H +#pragma once #include "EventHub.h" #include "InputDevice.h" #include "InputListener.h" #include "InputReaderContext.h" +#include "NotifyArgs.h" #include "StylusState.h" #include "VibrationElement.h" @@ -49,28 +49,30 @@ public: inline const std::string getDeviceName() const { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } - inline InputListenerInterface& getListener() { return getContext()->getListener(); } virtual uint32_t getSources() const = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent) = 0; - virtual void timeoutExpired(nsecs_t when); + [[nodiscard]] virtual std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes); + [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when); + [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0; + [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when); virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const; - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags); + [[nodiscard]] virtual std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, + ssize_t repeat, int32_t token); + [[nodiscard]] virtual std::list<NotifyArgs> cancelVibrate(int32_t token); virtual bool isVibrating(); virtual std::vector<int32_t> getVibratorIds(); - virtual void cancelTouch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] virtual std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime); virtual bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, std::chrono::microseconds maxBatchReportLatency); @@ -92,7 +94,7 @@ public: */ virtual bool updateMetaState(int32_t keyCode); - virtual void updateExternalStylusState(const StylusState& state); + [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state); virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; } virtual void updateLedState(bool reset) {} @@ -109,5 +111,3 @@ protected: }; } // namespace android - -#endif // _UI_INPUTREADER_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 7d30d0c1eb..929bf18ffe 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -103,9 +103,10 @@ void JoystickInputMapper::dump(std::string& dump) { } } -void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> JoystickInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); if (!changes) { // first time only // Collect all axes. @@ -145,12 +146,12 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) { Axis& axis = it->second; if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && + while (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE && haveAxis(nextGenericAxisId)) { nextGenericAxisId += 1; } - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + if (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) { axis.axisInfo.axis = nextGenericAxisId; nextGenericAxisId += 1; } else { @@ -164,6 +165,7 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration it++; } } + return out; } JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo, @@ -246,17 +248,18 @@ bool JoystickInputMapper::isCenteredAxis(int32_t axis) { } } -void JoystickInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> JoystickInputMapper::reset(nsecs_t when) { // Recenter all axes. for (std::pair<const int32_t, Axis>& pair : mAxes) { Axis& axis = pair.second; axis.resetValue(); } - InputMapper::reset(when); + return InputMapper::reset(when); } -void JoystickInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; switch (rawEvent->type) { case EV_ABS: { auto it = mAxes.find(rawEvent->code); @@ -298,16 +301,18 @@ void JoystickInputMapper::process(const RawEvent* rawEvent) { case EV_SYN: switch (rawEvent->code) { case SYN_REPORT: - sync(rawEvent->when, rawEvent->readTime, false /*force*/); + out += sync(rawEvent->when, rawEvent->readTime, false /*force*/); break; } break; } + return out; } -void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) { +std::list<NotifyArgs> JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) { + std::list<NotifyArgs> out; if (!filterAxes(force)) { - return; + return out; } int32_t metaState = getContext()->getGlobalMetaState(); @@ -340,13 +345,14 @@ void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) { displayId = getDeviceContext().getAssociatedViewport()->displayId; } - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, - 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, displayId, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {})); + return out; } void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index 307bf5b38b..72b8a528b5 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H -#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H +#pragma once #include "InputMapper.h" @@ -29,10 +28,11 @@ public: virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) override; - virtual void reset(nsecs_t when) override; - virtual void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; private: struct Axis { @@ -92,7 +92,7 @@ private: // Axes indexed by raw ABS_* axis index. std::unordered_map<int32_t, Axis> mAxes; - void sync(nsecs_t when, nsecs_t readTime, bool force); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime, bool force); bool haveAxis(int32_t axisId); void pruneAxes(bool ignoreExplicitlyMappedAxes); @@ -111,5 +111,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 2ac81781fa..44f0dfe3b6 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -20,71 +20,75 @@ #include "KeyboardInputMapper.h" +#include <ui/Rotation.h> + namespace android { // --- Static Definitions --- -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { - if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; +static int32_t rotateKeyCode(int32_t keyCode, ui::Rotation orientation) { + static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, + }; + + if (orientation != ui::ROTATION_0) { + for (const auto& rotation : KEYCODE_ROTATION_MAP) { + if (rotation[static_cast<size_t>(ui::ROTATION_0)] == keyCode) { + return rotation[static_cast<size_t>(orientation)]; } } } - return value; + return keyCode; } -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, - {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, - {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, - {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, - {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, - {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, - {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, - {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, -}; - -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], - size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; +static bool isSupportedScanCode(int32_t scanCode) { + // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses. + return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) || + scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 || + scanCode >= BTN_WHEEL; } -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, - {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, - {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, - {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, -}; - -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, - keyCodeRotationMapSize); +static bool isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + default: + return false; + } } // --- KeyboardInputMapper --- @@ -93,17 +97,15 @@ KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint int32_t keyboardType) : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {} -KeyboardInputMapper::~KeyboardInputMapper() {} - uint32_t KeyboardInputMapper::getSources() const { return mSource; } -int32_t KeyboardInputMapper::getOrientation() { +ui::Rotation KeyboardInputMapper::getOrientation() { if (mViewport) { return mViewport->orientation; } - return DISPLAY_ORIENTATION_0; + return ui::ROTATION_0; } int32_t KeyboardInputMapper::getDisplayId() { @@ -127,11 +129,10 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); } std::optional<DisplayViewport> KeyboardInputMapper::findViewport( - nsecs_t when, const InputReaderConfiguration* config) { + const InputReaderConfiguration* config) { if (getDeviceContext().getAssociatedViewport()) { return getDeviceContext().getAssociatedViewport(); } @@ -144,9 +145,10 @@ std::optional<DisplayViewport> KeyboardInputMapper::findViewport( return std::nullopt; } -void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); if (!changes) { // first time only // Configure basic parameters. @@ -154,122 +156,59 @@ void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = findViewport(when, config); - } -} - -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { - int32_t mapped = 0; - if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } + mViewport = findViewport(config); } + return out; } void KeyboardInputMapper::configureParameters() { mParameters.orientationAware = false; const PropertyMap& config = getDeviceContext().getConfiguration(); - config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } + config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware); mParameters.handlesKeyRepeat = false; - config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); + config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat); mParameters.doNotWakeByDefault = false; - config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault); + config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault); } -void KeyboardInputMapper::dumpParameters(std::string& dump) { +void KeyboardInputMapper::dumpParameters(std::string& dump) const { dump += INDENT3 "Parameters:\n"; dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); } -void KeyboardInputMapper::reset(nsecs_t when) { - mMetaState = AMETA_NONE; - mDownTime = 0; - mKeyDowns.clear(); - mCurrentHidUsage = 0; +std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) { + std::list<NotifyArgs> out = cancelAllDownKeys(when); + mHidUsageAccumulator.reset(); resetLedState(); - InputMapper::reset(when); + out += InputMapper::reset(when); + return out; } -void KeyboardInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; + mHidUsageAccumulator.process(*rawEvent); switch (rawEvent->type) { case EV_KEY: { int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode, - usageCode); + if (isSupportedScanCode(scanCode)) { + out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, + scanCode, mHidUsageAccumulator.consumeCurrentHidUsage()); } break; } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; - } - break; - } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } } + return out; } -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL || - (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || - (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - -void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, - int32_t usageCode) { +std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, + int32_t scanCode, int32_t usageCode) { + std::list<NotifyArgs> out; int32_t keyCode; int32_t keyMetaState; uint32_t policyFlags; @@ -281,6 +220,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, policyFlags = 0; } + nsecs_t downTime = when; + std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode); if (down) { // Rotate key codes according to orientation if needed. if (mParameters.orientationAware) { @@ -288,40 +229,39 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, } // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { + if (keyDownIndex) { // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; + keyCode = mKeyDowns[*keyDownIndex].keyCode; + downTime = mKeyDowns[*keyDownIndex].downTime; } else { // key down if ((policyFlags & POLICY_FLAG_VIRTUAL) && getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) { - return; + return out; } if (policyFlags & POLICY_FLAG_GESTURE) { - getDeviceContext().cancelTouch(when, readTime); + out += getDeviceContext().cancelTouch(when, readTime); } KeyDown keyDown; keyDown.keyCode = keyCode; keyDown.scanCode = scanCode; + keyDown.downTime = when; mKeyDowns.push_back(keyDown); } - - mDownTime = when; } else { // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { + if (keyDownIndex) { // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + keyCode = mKeyDowns[*keyDownIndex].keyCode; + downTime = mKeyDowns[*keyDownIndex].downTime; + mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex); } else { // key was not actually down ALOGI("Dropping key up from device %s because the key was not down. " "keyCode=%d, scanCode=%d", getDeviceName().c_str(), keyCode, scanCode); - return; + return out; } } @@ -333,8 +273,6 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, keyMetaState = mMetaState; } - nsecs_t downTime = mDownTime; - // Key down on external an keyboard should wake the device. // We don't do this for internal keyboards to prevent them from waking up in your pocket. // For internal keyboards and devices for which the default wake behavior is explicitly @@ -350,21 +288,22 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - getDisplayId(), policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener().notifyKey(&args); + out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, getDisplayId(), policyFlags, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, + downTime)); + return out; } -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { +std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) { size_t n = mKeyDowns.size(); for (size_t i = 0; i < n; i++) { if (mKeyDowns[i].scanCode == scanCode) { return i; } } - return -1; + return {}; } int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { @@ -379,9 +318,10 @@ int32_t KeyboardInputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) c return getDeviceContext().getKeyCodeForKeyLocation(locationKeyCode); } -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getDeviceContext().markSupportedKeyCodes(numCodes, keyCodes, outFlags); +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, + const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) { + return getDeviceContext().markSupportedKeyCodes(keyCodes, outFlags); } int32_t KeyboardInputMapper::getMetaState() { @@ -433,13 +373,12 @@ void KeyboardInputMapper::updateLedState(bool reset) { mMetaState |= getContext()->getLedMetaState(); constexpr int32_t META_NUM = 3; - const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK, - AKEYCODE_SCROLL_LOCK}; + const std::vector<int32_t> keyCodes{AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK, + AKEYCODE_SCROLL_LOCK}; const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON, AMETA_SCROLL_LOCK_ON}; std::array<uint8_t, META_NUM> flags = {0, 0, 0}; - bool hasKeyLayout = - getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data()); + bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodes, flags.data()); // If the device doesn't have the physical meta key it shouldn't generate the corresponding // meta state. if (hasKeyLayout) { @@ -473,4 +412,20 @@ std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() { return std::nullopt; } +std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { + std::list<NotifyArgs> out; + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, + systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource, + getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, + mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE, + mKeyDowns[i].downTime)); + } + mKeyDowns.clear(); + mMetaState = AMETA_NONE; + return out; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 378769639e..0526fd89de 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -14,9 +14,9 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H -#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H +#pragma once +#include "HidUsageAccumulator.h" #include "InputMapper.h" namespace android { @@ -24,82 +24,79 @@ namespace android { class KeyboardInputMapper : public InputMapper { public: KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); - - virtual uint32_t getSources() const override; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; - virtual void dump(std::string& dump) override; - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) override; - virtual void reset(nsecs_t when) override; - virtual void process(const RawEvent* rawEvent) override; - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) override; - virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override; - - virtual int32_t getMetaState() override; - virtual bool updateMetaState(int32_t keyCode) override; - virtual std::optional<int32_t> getAssociatedDisplayId() override; - virtual void updateLedState(bool reset); + ~KeyboardInputMapper() override = default; + + uint32_t getSources() const override; + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + void dump(std::string& dump) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; + bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) override; + int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override; + + int32_t getMetaState() override; + bool updateMetaState(int32_t keyCode) override; + std::optional<int32_t> getAssociatedDisplayId() override; + void updateLedState(bool reset) override; private: // The current viewport. - std::optional<DisplayViewport> mViewport; + std::optional<DisplayViewport> mViewport{}; struct KeyDown { - int32_t keyCode; - int32_t scanCode; + nsecs_t downTime{}; + int32_t keyCode{}; + int32_t scanCode{}; }; - uint32_t mSource; - int32_t mKeyboardType; + uint32_t mSource{}; + int32_t mKeyboardType{}; - std::vector<KeyDown> mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down + std::vector<KeyDown> mKeyDowns{}; // keys that are down + int32_t mMetaState{}; - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + HidUsageAccumulator mHidUsageAccumulator; struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on + bool avail{}; // led is available + bool on{}; // we think the led is currently on }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; + LedState mCapsLockLedState{}; + LedState mNumLockLedState{}; + LedState mScrollLockLedState{}; // Immutable configuration parameters. struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - bool doNotWakeByDefault; - } mParameters; + bool orientationAware{}; + bool handlesKeyRepeat{}; + bool doNotWakeByDefault{}; + } mParameters{}; void configureParameters(); - void dumpParameters(std::string& dump); + void dumpParameters(std::string& dump) const; - int32_t getOrientation(); + ui::Rotation getOrientation(); int32_t getDisplayId(); - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - - void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode); + [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down, + int32_t scanCode, int32_t usageCode); bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - ssize_t findKeyDown(int32_t scanCode); + std::optional<size_t> findKeyDownIndex(int32_t scanCode); void resetLedState(); void initializeLedState(LedState& ledState, int32_t led); void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); - std::optional<DisplayViewport> findViewport(nsecs_t when, - const InputReaderConfiguration* config); + std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config); + [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); }; } // namespace android - -#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index cc323ad42e..633efc6047 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -16,9 +16,8 @@ #include "../Macros.h" -#include "MultiTouchInputMapper.h" - #include <android/sysprop/InputProperties.sysprop.h> +#include "MultiTouchInputMapper.h" namespace android { @@ -27,196 +26,6 @@ namespace android { // Maximum number of slots supported when using the slot-based Multitouch Protocol B. static constexpr size_t MAX_SLOTS = 32; -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mSlots(nullptr), - mSlotCount(0), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { - delete[] mSlots; -} - -void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, - bool usingSlotsProtocol) { - mSlotCount = slotCount; - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - - delete[] mSlots; - mSlots = new Slot[slotCount]; - - mCurrentSlot = -1; - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; - } else { - ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); - } - } -} - -void MultiTouchMotionAccumulator::resetSlots() { - if (mSlots) { - for (size_t i = 0; i < mSlotCount; i++) { - mSlots[i].clear(); - } - } - mCurrentSlot = -1; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { - if (DEBUG_POINTERS) { - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); - } - } - } else { - Slot* slot = &mSlots[mCurrentSlot]; - // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of - // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while - // updating the slot. - if (!mUsingSlotsProtocol) { - slot->mInUse = true; - } - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot->mAbsMTPositionX = rawEvent->value; - warnIfNotInUse(*rawEvent, *slot); - break; - case ABS_MT_POSITION_Y: - slot->mAbsMTPositionY = rawEvent->value; - warnIfNotInUse(*rawEvent, *slot); - break; - case ABS_MT_TOUCH_MAJOR: - slot->mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->mAbsMTTouchMinor = rawEvent->value; - slot->mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot->mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->mAbsMTWidthMinor = rawEvent->value; - slot->mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot->mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot->mInUse = false; - } else { - slot->mInUse = true; - slot->mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot->mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->mAbsMTToolType = rawEvent->value; - slot->mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - resetSlots(); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - -void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { - if (!slot.mInUse) { - ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", - event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId); - } -} - -// --- MultiTouchMotionAccumulator::Slot --- - -MultiTouchMotionAccumulator::Slot::Slot() { - clear(); -} - -void MultiTouchMotionAccumulator::Slot::clear() { - mInUse = false; - mHaveAbsMTTouchMinor = false; - mHaveAbsMTWidthMinor = false; - mHaveAbsMTToolType = false; - mAbsMTPositionX = 0; - mAbsMTPositionY = 0; - mAbsMTTouchMajor = 0; - mAbsMTTouchMinor = 0; - mAbsMTWidthMajor = 0; - mAbsMTWidthMinor = 0; - mAbsMTOrientation = 0; - mAbsMTTrackingId = -1; - mAbsMTPressure = 0; - mAbsMTDistance = 0; - mAbsMTToolType = 0; -} - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - case MT_TOOL_PALM: - return AMOTION_EVENT_TOOL_TYPE_PALM; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - // --- MultiTouchInputMapper --- MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext) @@ -224,20 +33,21 @@ MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext) MultiTouchInputMapper::~MultiTouchInputMapper() {} -void MultiTouchInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) { // The evdev multi-touch protocol does not allow userspace applications to query the initial or // current state of the pointers at any time. This means if we clear our accumulated state when // resetting the input mapper, there's no way to rebuild the full initial state of the pointers. // We can only wait for updates to all the pointers and axes. Rather than clearing the state and // rebuilding the state from scratch, we work around this kernel API limitation by never // fully clearing any state specific to the multi-touch protocol. - TouchInputMapper::reset(when); + return TouchInputMapper::reset(when); } -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); +std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent); mMultiTouchMotionAccumulator.process(rawEvent); + return out; } std::optional<int32_t> MultiTouchInputMapper::getActiveBitId( @@ -261,14 +71,14 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { mHavePointerIds = true; for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const MultiTouchMotionAccumulator::Slot* inSlot = + const MultiTouchMotionAccumulator::Slot& inSlot = mMultiTouchMotionAccumulator.getSlot(inIndex); - if (!inSlot->isInUse()) { + if (!inSlot.isInUse()) { continue; } - if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) { - std::optional<int32_t> id = getActiveBitId(*inSlot); + if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) { + std::optional<int32_t> id = getActiveBitId(inSlot); if (id) { outState->rawPointerData.canceledIdBits.markBit(id.value()); } @@ -289,24 +99,36 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { } RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; - outPointer.x = inSlot->getX(); - outPointer.y = inSlot->getY(); - outPointer.pressure = inSlot->getPressure(); - outPointer.touchMajor = inSlot->getTouchMajor(); - outPointer.touchMinor = inSlot->getTouchMinor(); - outPointer.toolMajor = inSlot->getToolMajor(); - outPointer.toolMinor = inSlot->getToolMinor(); - outPointer.orientation = inSlot->getOrientation(); - outPointer.distance = inSlot->getDistance(); + outPointer.x = inSlot.getX(); + outPointer.y = inSlot.getY(); + outPointer.pressure = inSlot.getPressure(); + outPointer.touchMajor = inSlot.getTouchMajor(); + outPointer.touchMinor = inSlot.getTouchMinor(); + outPointer.toolMajor = inSlot.getToolMajor(); + outPointer.toolMinor = inSlot.getToolMinor(); + outPointer.orientation = inSlot.getOrientation(); + outPointer.distance = inSlot.getDistance(); outPointer.tiltX = 0; outPointer.tiltY = 0; - outPointer.toolType = inSlot->getToolType(); + outPointer.toolType = inSlot.getToolType(); if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { outPointer.toolType = mTouchButtonAccumulator.getToolType(); if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; } + } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) { + mStylusMtToolSeen = true; + // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically + // re-configure this input device so that we add SOURCE_STYLUS if we haven't already. + // This is to cover the case where we cannot reliably detect whether a multi-touch + // device will ever produce stylus events when it is initially being configured. + if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) { + // Add the stylus source immediately so that it is included in any events generated + // before we have a chance to re-configure the device. + mSource |= AINPUT_SOURCE_STYLUS; + bumpGeneration(); + } } if (shouldSimulateStylusWithTouch() && outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { @@ -315,12 +137,12 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); + (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0)); outPointer.isHovering = isHovering; // Assign pointer id using tracking id if available. if (mHavePointerIds) { - int32_t trackingId = inSlot->getTrackingId(); + int32_t trackingId = inSlot.getTrackingId(); int32_t id = -1; if (trackingId >= 0) { for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { @@ -388,7 +210,7 @@ void MultiTouchInputMapper::configureRawPointerAxes() { } bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() || + return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() || shouldSimulateStylusWithTouch(); } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index e7d935082a..5f8bccf9e6 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -14,88 +14,20 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H +#pragma once #include "TouchInputMapper.h" +#include "accumulator/MultiTouchMotionAccumulator.h" namespace android { -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; - } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; - } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse; - bool mHaveAbsMTTouchMinor; - bool mHaveAbsMTWidthMinor; - bool mHaveAbsMTToolType; - - int32_t mAbsMTPositionX; - int32_t mAbsMTPositionY; - int32_t mAbsMTTouchMajor; - int32_t mAbsMTTouchMinor; - int32_t mAbsMTWidthMajor; - int32_t mAbsMTWidthMinor; - int32_t mAbsMTOrientation; - int32_t mAbsMTTrackingId; - int32_t mAbsMTPressure; - int32_t mAbsMTDistance; - int32_t mAbsMTToolType; - - Slot(); - void clear(); - }; - - MultiTouchMotionAccumulator(); - ~MultiTouchMotionAccumulator(); - - void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlotCount; } - inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - -private: - int32_t mCurrentSlot; - Slot* mSlots; - size_t mSlotCount; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void resetSlots(); - void warnIfNotInUse(const RawEvent& event, const Slot& slot); -}; - class MultiTouchInputMapper : public TouchInputMapper { public: explicit MultiTouchInputMapper(InputDeviceContext& deviceContext); ~MultiTouchInputMapper() override; - void reset(nsecs_t when) override; - void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; protected: void syncTouch(nsecs_t when, RawState* outState) override; @@ -118,8 +50,8 @@ private: // Specifies the pointer id bits that are in use, and their associated tracking id. BitSet32 mPointerIdBits; int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; + + bool mStylusMtToolSeen{false}; }; } // namespace android - -#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index eca25f6e6a..19a79d7751 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -25,7 +25,7 @@ namespace android { RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext) - : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) { + : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) { mSource = AINPUT_SOURCE_ROTARY_ENCODER; } @@ -40,10 +40,10 @@ void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { float res = 0.0f; - if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) { + if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) { ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); } - if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"), + if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor", mScalingFactor)) { ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," "default to 1.0!\n"); @@ -60,9 +60,10 @@ void RotaryEncoderInputMapper::dump(std::string& dump) { toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); } -void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> RotaryEncoderInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); if (!changes) { mRotaryEncoderScrollAccumulator.configure(getDeviceContext()); } @@ -72,65 +73,69 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfigur if (internalViewport) { mOrientation = internalViewport->orientation; } else { - mOrientation = DISPLAY_ORIENTATION_0; + mOrientation = ui::ROTATION_0; } } + return out; } -void RotaryEncoderInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) { mRotaryEncoderScrollAccumulator.reset(getDeviceContext()); - InputMapper::reset(when); + return InputMapper::reset(when); } -void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; mRotaryEncoderScrollAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when, rawEvent->readTime); + out += sync(rawEvent->when, rawEvent->readTime); } + return out; } -void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); bool scrolled = scroll != 0; - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDeviceContext().isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - // Send motion event. if (scrolled) { int32_t metaState = getContext()->getGlobalMetaState(); + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + if (mOrientation == ui::ROTATION_180) { + scroll = -scroll; + } + + PointerCoords pointerCoords; + pointerCoords.clear(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, - 0, metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener().notifyMotion(&scrollArgs); + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + uint32_t policyFlags = 0; + if (getDeviceContext().isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + out.push_back( + NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {})); } mRotaryEncoderScrollAccumulator.finishSync(); + return out; } } // namespace android diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 1859355a93..cb5fd88209 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -14,8 +14,9 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H -#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H +#pragma once + +#include <ui/Rotation.h> #include "CursorScrollAccumulator.h" #include "InputMapper.h" @@ -30,21 +31,20 @@ public: virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) override; - virtual void reset(nsecs_t when) override; - virtual void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; private: CursorScrollAccumulator mRotaryEncoderScrollAccumulator; int32_t mSource; float mScalingFactor; - int32_t mOrientation; + ui::Rotation mOrientation; - void sync(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime); }; } // namespace android - -#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index b01c2bc13f..d81022f6e5 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -64,7 +64,7 @@ uint32_t SensorInputMapper::getSources() const { template <typename T> bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) { const auto& config = getDeviceContext().getConfiguration(); - return config.tryGetProperty(String8(keyName.c_str()), outValue); + return config.tryGetProperty(keyName, outValue); } void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode, @@ -122,9 +122,10 @@ void SensorInputMapper::dump(std::string& dump) { } } -void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> SensorInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); if (!changes) { // first time only mDeviceEnabled = true; @@ -158,6 +159,7 @@ void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } } } + return out; } SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo, @@ -185,7 +187,7 @@ SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo, return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter); } -void SensorInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> SensorInputMapper::reset(nsecs_t when) { // Recenter all axes. for (std::pair<const int32_t, Axis>& pair : mAxes) { Axis& axis = pair.second; @@ -193,7 +195,7 @@ void SensorInputMapper::reset(nsecs_t when) { } mHardwareTimestamp = 0; mPrevMscTime = 0; - InputMapper::reset(when); + return InputMapper::reset(when); } SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType, @@ -256,7 +258,8 @@ void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime mPrevMscTime = static_cast<uint32_t>(mscTime); } -void SensorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; switch (rawEvent->type) { case EV_ABS: { auto it = mAxes.find(rawEvent->code); @@ -274,7 +277,7 @@ void SensorInputMapper::process(const RawEvent* rawEvent) { Axis& axis = pair.second; axis.currentValue = axis.newValue; } - sync(rawEvent->when, false /*force*/); + out += sync(rawEvent->when, false /*force*/); break; } break; @@ -287,6 +290,7 @@ void SensorInputMapper::process(const RawEvent* rawEvent) { break; } } + return out; } bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) { @@ -375,7 +379,8 @@ void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) { } } -void SensorInputMapper::sync(nsecs_t when, bool force) { +std::list<NotifyArgs> SensorInputMapper::sync(nsecs_t when, bool force) { + std::list<NotifyArgs> out; for (auto& [sensorType, sensor] : mSensors) { // Skip if sensor not enabled if (!sensor.enabled) { @@ -405,17 +410,17 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { // Convert to Android unit convertFromLinuxToAndroid(values, sensorType); // Notify dispatcher for sensor event - NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(), - AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy, - sensor.accuracy != - sensor.sensorInfo.accuracy /* accuracyChanged */, - timestamp /* hwTimestamp */, values); - - getListener().notifySensor(&args); + out.push_back(NotifySensorArgs(getContext()->getNextId(), when, getDeviceId(), + AINPUT_SOURCE_SENSOR, sensorType, + sensor.sensorInfo.accuracy, + sensor.accuracy != + sensor.sensorInfo.accuracy /* accuracyChanged */, + timestamp /* hwTimestamp */, values)); sensor.lastSampleTimeNs = timestamp; sensor.accuracy = sensor.sensorInfo.accuracy; } } + return out; } } // namespace android diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h index 27a61771f2..457567ba7b 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.h +++ b/services/inputflinger/reader/mapper/SensorInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H -#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H +#pragma once #include "InputMapper.h" @@ -31,9 +30,11 @@ public: uint32_t getSources() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; - void reset(nsecs_t when) override; - void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, std::chrono::microseconds maxBatchReportLatency) override; void disableSensor(InputDeviceSensorType sensorType) override; @@ -117,7 +118,7 @@ private: // Sensor list std::unordered_map<InputDeviceSensorType, Sensor> mSensors; - void sync(nsecs_t when, bool force); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force); template <typename T> bool tryGetProperty(std::string keyName, T& outValue); @@ -133,5 +134,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp index 4fff9bebb5..13ad224111 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -23,16 +23,17 @@ SingleTouchInputMapper::SingleTouchInputMapper(InputDeviceContext& deviceContext SingleTouchInputMapper::~SingleTouchInputMapper() {} -void SingleTouchInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> SingleTouchInputMapper::reset(nsecs_t when) { mSingleTouchMotionAccumulator.reset(getDeviceContext()); - TouchInputMapper::reset(when); + return TouchInputMapper::reset(when); } -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); +std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent); mSingleTouchMotionAccumulator.process(rawEvent); + return out; } void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h index 9cb3f67f20..662e6bca25 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H +#pragma once #include "SingleTouchMotionAccumulator.h" #include "TouchInputMapper.h" @@ -27,8 +26,8 @@ public: explicit SingleTouchInputMapper(InputDeviceContext& deviceContext); ~SingleTouchInputMapper() override; - void reset(nsecs_t when) override; - void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; protected: void syncTouch(nsecs_t when, RawState* outState) override; @@ -40,5 +39,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index ebb5de66ed..c9101cadc6 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -29,7 +29,8 @@ uint32_t SwitchInputMapper::getSources() const { return AINPUT_SOURCE_SWITCH; } -void SwitchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out; switch (rawEvent->type) { case EV_SW: processSwitch(rawEvent->code, rawEvent->value); @@ -37,9 +38,10 @@ void SwitchInputMapper::process(const RawEvent* rawEvent) { case EV_SYN: if (rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); + out += sync(rawEvent->when); } } + return out; } void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { @@ -53,15 +55,16 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { } } -void SwitchInputMapper::sync(nsecs_t when) { +std::list<NotifyArgs> SwitchInputMapper::sync(nsecs_t when) { + std::list<NotifyArgs> out; if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, - updatedSwitchValues, mUpdatedSwitchMask); - getListener().notifySwitch(&args); + out.push_back(NotifySwitchArgs(getContext()->getNextId(), when, 0 /*policyFlags*/, + updatedSwitchValues, mUpdatedSwitchMask)); mUpdatedSwitchMask = 0; } + return out; } int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index 64b9aa27b4..06d6504684 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H +#pragma once #include "InputMapper.h" @@ -27,7 +26,7 @@ public: virtual ~SwitchInputMapper(); virtual uint32_t getSources() const override; - virtual void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override; virtual void dump(std::string& dump) override; @@ -37,9 +36,7 @@ private: uint32_t mUpdatedSwitchMask; void processSwitch(int32_t switchCode, int32_t switchValue); - void sync(nsecs_t when); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when); }; } // namespace android - -#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp new file mode 100644 index 0000000000..c12e95dfa0 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 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/DisplayViewport.h> +#include <stdint.h> +#include <ui/Rotation.h> + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderContext.h" + +namespace android { + +namespace { + +[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey( + InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { + std::list<NotifyArgs> out; + if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && + (currentButtonState & buttonState)) || + (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && + !(currentButtonState & buttonState))) { + out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source, + displayId, policyFlags, action, 0, keyCode, 0, + context->getGlobalMetaState(), when)); + } + return out; +} + +} // namespace + +ui::Rotation getInverseRotation(ui::Rotation orientation) { + switch (orientation) { + case ui::ROTATION_90: + return ui::ROTATION_270; + case ui::ROTATION_270: + return ui::ROTATION_90; + default: + return orientation; + } +} + +void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) { + float temp; + switch (orientation) { + case ui::ROTATION_90: + temp = *deltaX; + *deltaX = *deltaY; + *deltaY = -temp; + break; + + case ui::ROTATION_180: + *deltaX = -*deltaX; + *deltaY = -*deltaY; + break; + + case ui::ROTATION_270: + temp = *deltaX; + *deltaX = -*deltaY; + *deltaY = temp; + break; + + default: + break; + } +} + +bool isPointerDown(int32_t buttonState) { + return buttonState & + (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY); +} + +[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys( + InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t lastButtonState, int32_t currentButtonState) { + std::list<NotifyArgs> out; + out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, + policyFlags, lastButtonState, currentButtonState, + AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK); + out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, + policyFlags, lastButtonState, currentButtonState, + AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD); + return out; +} + +std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening( + const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime, + nsecs_t lastEventTime) { + if (identifier.bus != BUS_BLUETOOTH) { + return {currentEventTime, readTime}; + } + + // Assume the fastest rate at which a Bluetooth touch device can report input events is one + // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device + // will be separated by at least this amount. + constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); + // We define a maximum smoothing time delta so that we don't generate events too far into the + // future. + constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); + const nsecs_t smoothenedEventTime = + std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA), + currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA); + // If we are modifying the event time, treat this event as a synthetically generated event for + // latency tracking purposes and use the event time as the read time (zero read latency). + const nsecs_t smoothenedReadTime = + smoothenedEventTime != currentEventTime ? currentEventTime : readTime; + return {smoothenedEventTime, smoothenedReadTime}; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 31a3d2e172..3023e686c2 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -14,11 +14,11 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H -#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#pragma once #include <input/DisplayViewport.h> #include <stdint.h> +#include <ui/Rotation.h> #include "EventHub.h" #include "InputListener.h" @@ -26,78 +26,30 @@ namespace android { -// --- Static Definitions --- +ui::Rotation getInverseRotation(ui::Rotation orientation); -static int32_t getInverseRotation(int32_t orientation) { - switch (orientation) { - case DISPLAY_ORIENTATION_90: - return DISPLAY_ORIENTATION_270; - case DISPLAY_ORIENTATION_270: - return DISPLAY_ORIENTATION_90; - default: - return orientation; - } -} - -static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { - float temp; - switch (orientation) { - case DISPLAY_ORIENTATION_90: - temp = *deltaX; - *deltaX = *deltaY; - *deltaY = -temp; - break; - - case DISPLAY_ORIENTATION_180: - *deltaX = -*deltaX; - *deltaY = -*deltaY; - break; - - case DISPLAY_ORIENTATION_270: - temp = *deltaX; - *deltaX = -*deltaY; - *deltaY = temp; - break; - - default: - break; - } -} +void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY); // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. -static bool isPointerDown(int32_t buttonState) { - return buttonState & - (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | - AMOTION_EVENT_BUTTON_TERTIARY); -} - -static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, - nsecs_t readTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { - if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && - (currentButtonState & buttonState)) || - (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && - !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener().notifyKey(&args); - } -} - -static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, - nsecs_t readTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, - AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, - AKEYCODE_FORWARD); -} +bool isPointerDown(int32_t buttonState); + +[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys( + InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t lastButtonState, int32_t currentButtonState); + +// For devices connected over Bluetooth, although they may produce events at a consistent rate, +// the events might end up reaching Android in a "batched" manner through the Bluetooth +// stack, where a few events may be clumped together and processed around the same time. +// In this case, if the input device or its driver does not send or process the actual event +// generation timestamps, the event time will set to whenever the kernel received the event. +// When the timestamp deltas are minuscule for these batched events, any changes in x or y +// coordinates result in extremely large instantaneous velocities, which can negatively impact +// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps +// differ by at least a minimum delta value. +std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening( + const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime, + nsecs_t lastEventTime); } // namespace android - -#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ed3b0eccf0..cefc44ef7a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -21,31 +21,41 @@ #include "TouchInputMapper.h" #include <ftl/enum.h> +#include <input/PrintTools.h> #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" #include "TouchButtonAccumulator.h" #include "TouchCursorInputMapperCommon.h" +#include "ui/Rotation.h" namespace android { // --- Constants --- -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - // Artificial latency on synthetic events created from stylus data without corresponding touch // data. static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); +// Minimum width between two pointers to determine a gesture as freeform gesture in mm +static const float MIN_FREEFORM_GESTURE_WIDTH_IN_MILLIMETER = 30; // --- Static Definitions --- static const DisplayViewport kUninitializedViewport; +static std::string toString(const Rect& rect) { + return base::StringPrintf("Rect{%d, %d, %d, %d}", rect.left, rect.top, rect.right, rect.bottom); +} + +static std::string toString(const ui::Size& size) { + return base::StringPrintf("%dx%d", size.width, size.height); +} + +static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) { + // Consider all four sides as "inclusive". + return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; +} + template <typename T> inline static void swap(T& a, T& b) { T temp = a; @@ -71,53 +81,33 @@ inline static int32_t signExtendNybble(int32_t value) { return value >= 8 ? value - 16 : value; } -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - -// --- RawPointerData --- - -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} +static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo( + const DisplayViewport& viewport, ui::Rotation naturalOrientation) { + ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight}; + if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) { + std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height); + } -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - canceledIdBits = other.canceledIdBits; + ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation), + rotatedDisplaySize.width, rotatedDisplaySize.height); - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; + Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight, + viewport.physicalBottom}; + physicalFrame = rotate.transform(physicalFrame); - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; + LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid()); + if (physicalFrame.isEmpty()) { + ALOGE("Viewport is not set properly: %s", viewport.toString().c_str()); + physicalFrame.right = + physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width()); + physicalFrame.bottom = + physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height()); } + return {rotatedDisplaySize, physicalFrame}; } +// --- RawPointerData --- + void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { float x = 0, y = 0; uint32_t count = touchingIdBits.count(); @@ -135,48 +125,14 @@ void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) con *outY = y; } -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); - canceledIdBits.clear(); - validIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - validIdBits = other.validIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - // --- TouchInputMapper --- TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext), + mTouchButtonAccumulator(deviceContext), mSource(0), mDeviceMode(DeviceMode::DISABLED), - mDisplayWidth(-1), - mDisplayHeight(-1), - mPhysicalWidth(-1), - mPhysicalHeight(-1), - mPhysicalLeft(0), - mPhysicalTop(0), - mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {} + mInputDeviceOrientation(ui::ROTATION_0) {} TouchInputMapper::~TouchInputMapper() {} @@ -187,73 +143,74 @@ uint32_t TouchInputMapper::getSources() const { void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - if (mDeviceMode != DeviceMode::DISABLED) { - info->addMotionRange(mOrientedRanges.x); - info->addMotionRange(mOrientedRanges.y); - info->addMotionRange(mOrientedRanges.pressure); + if (mDeviceMode == DeviceMode::DISABLED) { + return; + } - if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) { - // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode. - // - // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative - // motion, i.e. the hardware dimensions, as the finger could move completely across the - // touchpad in one sample cycle. - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, - y.fuzz, y.resolution); - } + info->addMotionRange(mOrientedRanges.x); + info->addMotionRange(mOrientedRanges.y); + info->addMotionRange(mOrientedRanges.pressure); - if (mOrientedRanges.haveSize) { - info->addMotionRange(mOrientedRanges.size); - } + if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) { + // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode. + // + // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative + // motion, i.e. the hardware dimensions, as the finger could move completely across the + // touchpad in one sample cycle. + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz, + x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz, + y.resolution); + } - if (mOrientedRanges.haveTouchSize) { - info->addMotionRange(mOrientedRanges.touchMajor); - info->addMotionRange(mOrientedRanges.touchMinor); - } + if (mOrientedRanges.size) { + info->addMotionRange(*mOrientedRanges.size); + } - if (mOrientedRanges.haveToolSize) { - info->addMotionRange(mOrientedRanges.toolMajor); - info->addMotionRange(mOrientedRanges.toolMinor); - } + if (mOrientedRanges.touchMajor) { + info->addMotionRange(*mOrientedRanges.touchMajor); + info->addMotionRange(*mOrientedRanges.touchMinor); + } - if (mOrientedRanges.haveOrientation) { - info->addMotionRange(mOrientedRanges.orientation); - } + if (mOrientedRanges.toolMajor) { + info->addMotionRange(*mOrientedRanges.toolMajor); + info->addMotionRange(*mOrientedRanges.toolMinor); + } - if (mOrientedRanges.haveDistance) { - info->addMotionRange(mOrientedRanges.distance); - } + if (mOrientedRanges.orientation) { + info->addMotionRange(*mOrientedRanges.orientation); + } - if (mOrientedRanges.haveTilt) { - info->addMotionRange(mOrientedRanges.tilt); - } + if (mOrientedRanges.distance) { + info->addMotionRange(*mOrientedRanges.distance); + } - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) { - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - } - info->setButtonUnderPad(mParameters.hasButtonUnderPad); + if (mOrientedRanges.tilt) { + info->addMotionRange(*mOrientedRanges.tilt); + } + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); } + if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, x.fuzz, + x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, y.fuzz, + y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, x.fuzz, + x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, y.fuzz, + y.resolution); + } + info->setButtonUnderPad(mParameters.hasButtonUnderPad); + info->setSupportsUsi(mParameters.supportsUsi); } void TouchInputMapper::dump(std::string& dump) { @@ -328,9 +285,12 @@ void TouchInputMapper::dump(std::string& dump) { dump += INDENT3 "Stylus Fusion:\n"; dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n", + toString(mFusedStylusPointerId).c_str()); dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", mExternalStylusFusionTimeout); + dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x", + mExternalStylusButtonsApplied); dump += INDENT3 "External Stylus State:\n"; dumpStylusState(dump, mExternalStylusState); @@ -344,9 +304,10 @@ void TouchInputMapper::dump(std::string& dump) { } } -void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); +std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + std::list<NotifyArgs> out = InputMapper::configure(when, config, changes); mConfig = *config; @@ -356,7 +317,7 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c // Configure common accumulators. mCursorScrollAccumulator.configure(getDeviceContext()); - mTouchButtonAccumulator.configure(getDeviceContext()); + mTouchButtonAccumulator.configure(); // Configure absolute axis information. configureRawPointerAxes(); @@ -392,15 +353,13 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c } if (changes && resetNeeded) { - // If the device needs to be reset, cancel any ongoing gestures and reset the state. - cancelTouch(when, when); - reset(when); + out += reset(when); // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener().notifyDeviceReset(&args); + out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); } + return out; } void TouchInputMapper::resolveExternalStylusPresence() { @@ -421,15 +380,15 @@ void TouchInputMapper::configureParameters() { ? Parameters::GestureMode::SINGLE_TOUCH : Parameters::GestureMode::MULTI_TOUCH; - String8 gestureModeString; - if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"), + std::string gestureModeString; + if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode", gestureModeString)) { if (gestureModeString == "single-touch") { mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH; } else if (gestureModeString == "multi-touch") { mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH; } else if (gestureModeString != "default") { - ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); + ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str()); } } @@ -439,11 +398,6 @@ void TouchInputMapper::configureParameters() { } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) { // The device is a pointing device like a track pad. mParameters.deviceType = Parameters::DeviceType::POINTER; - } else if (getDeviceContext().hasRelativeAxis(REL_X) || - getDeviceContext().hasRelativeAxis(REL_Y)) { - // The device is a cursor device with a touch pad attached. - // By default don't use the touch pad to move the pointer. - mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD; } else { // The device is a touch pad of unknown purpose. mParameters.deviceType = Parameters::DeviceType::POINTER; @@ -451,40 +405,38 @@ void TouchInputMapper::configureParameters() { mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD); - String8 deviceTypeString; - if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"), + std::string deviceTypeString; + if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN; - } else if (deviceTypeString == "touchPad") { - mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD; } else if (deviceTypeString == "touchNavigation") { mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION; } else if (deviceTypeString == "pointer") { mParameters.deviceType = Parameters::DeviceType::POINTER; } else if (deviceTypeString != "default") { - ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str()); } } mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN; - getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"), + getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware", mParameters.orientationAware); - mParameters.orientation = Parameters::Orientation::ORIENTATION_0; - String8 orientationString; - if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"), + mParameters.orientation = ui::ROTATION_0; + std::string orientationString; + if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation", orientationString)) { if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) { ALOGW("The configuration 'touch.orientation' is only supported for touchscreens."); } else if (orientationString == "ORIENTATION_90") { - mParameters.orientation = Parameters::Orientation::ORIENTATION_90; + mParameters.orientation = ui::ROTATION_90; } else if (orientationString == "ORIENTATION_180") { - mParameters.orientation = Parameters::Orientation::ORIENTATION_180; + mParameters.orientation = ui::ROTATION_180; } else if (orientationString == "ORIENTATION_270") { - mParameters.orientation = Parameters::Orientation::ORIENTATION_270; + mParameters.orientation = ui::ROTATION_270; } else if (orientationString != "ORIENTATION_0") { - ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string()); + ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str()); } } @@ -496,8 +448,8 @@ void TouchInputMapper::configureParameters() { mParameters.hasAssociatedDisplay = true; if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) { mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal(); - String8 uniqueDisplayId; - getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"), + std::string uniqueDisplayId; + getDeviceContext().getConfiguration().tryGetProperty("touch.displayId", uniqueDisplayId); mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); } @@ -510,7 +462,15 @@ void TouchInputMapper::configureParameters() { // Normally we don't do this for internal touch screens to prevent them from waking // up in your pocket but you can enable it using the input device configuration. mParameters.wake = getDeviceContext().isExternal(); - getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); + getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake); + + mParameters.supportsUsi = false; + getDeviceContext().getConfiguration().tryGetProperty("touch.supportsUsi", + mParameters.supportsUsi); + + mParameters.enableForInactiveViewport = false; + getDeviceContext().getConfiguration().tryGetProperty("touch.enableForInactiveViewport", + mParameters.enableForInactiveViewport); } void TouchInputMapper::dumpParameters(std::string& dump) { @@ -527,6 +487,9 @@ void TouchInputMapper::dumpParameters(std::string& dump) { mParameters.uniqueDisplayId.c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n"; + dump += StringPrintf(INDENT4 "SupportsUsi: %s\n", toString(mParameters.supportsUsi)); + dump += StringPrintf(INDENT4 "EnableForInactiveViewport: %s\n", + toString(mParameters.enableForInactiveViewport)); } void TouchInputMapper::configureRawPointerAxes() { @@ -634,7 +597,7 @@ void TouchInputMapper::initializeSizeRanges() { } // Size of diagonal axis. - const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight); + const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height); // Size factors. if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { @@ -645,57 +608,58 @@ void TouchInputMapper::initializeSizeRanges() { mSizeScale = 0.0f; } - mOrientedRanges.haveTouchSize = true; - mOrientedRanges.haveToolSize = true; - mOrientedRanges.haveSize = true; + mOrientedRanges.touchMajor = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR, + .source = mSource, + .min = 0, + .max = diagonalSize, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; - mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; - mOrientedRanges.touchMajor.source = mSource; - mOrientedRanges.touchMajor.min = 0; - mOrientedRanges.touchMajor.max = diagonalSize; - mOrientedRanges.touchMajor.flat = 0; - mOrientedRanges.touchMajor.fuzz = 0; - mOrientedRanges.touchMajor.resolution = 0; if (mRawPointerAxes.touchMajor.valid) { mRawPointerAxes.touchMajor.resolution = clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution); - mOrientedRanges.touchMajor.resolution = mRawPointerAxes.touchMajor.resolution; + mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution; } mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; - mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; + mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; if (mRawPointerAxes.touchMinor.valid) { mRawPointerAxes.touchMinor.resolution = clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution); - mOrientedRanges.touchMinor.resolution = mRawPointerAxes.touchMinor.resolution; - } - - mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; - mOrientedRanges.toolMajor.source = mSource; - mOrientedRanges.toolMajor.min = 0; - mOrientedRanges.toolMajor.max = diagonalSize; - mOrientedRanges.toolMajor.flat = 0; - mOrientedRanges.toolMajor.fuzz = 0; - mOrientedRanges.toolMajor.resolution = 0; + mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution; + } + + mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_TOOL_MAJOR, + .source = mSource, + .min = 0, + .max = diagonalSize, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; if (mRawPointerAxes.toolMajor.valid) { mRawPointerAxes.toolMajor.resolution = clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution); - mOrientedRanges.toolMajor.resolution = mRawPointerAxes.toolMajor.resolution; + mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution; } mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; - mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; + mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR; if (mRawPointerAxes.toolMinor.valid) { mRawPointerAxes.toolMinor.resolution = clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution); - mOrientedRanges.toolMinor.resolution = mRawPointerAxes.toolMinor.resolution; + mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution; } if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) { - mOrientedRanges.touchMajor.resolution *= mGeometricScale; - mOrientedRanges.touchMinor.resolution *= mGeometricScale; - mOrientedRanges.toolMajor.resolution *= mGeometricScale; - mOrientedRanges.toolMinor.resolution *= mGeometricScale; + mOrientedRanges.touchMajor->resolution *= mGeometricScale; + mOrientedRanges.touchMinor->resolution *= mGeometricScale; + mOrientedRanges.toolMajor->resolution *= mGeometricScale; + mOrientedRanges.toolMinor->resolution *= mGeometricScale; } else { // Support for other calibrations can be added here. ALOGW("%s calibration is not supported for size ranges at the moment. " @@ -703,19 +667,21 @@ void TouchInputMapper::initializeSizeRanges() { ftl::enum_string(mCalibration.sizeCalibration).c_str()); } - mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; - mOrientedRanges.size.source = mSource; - mOrientedRanges.size.min = 0; - mOrientedRanges.size.max = 1.0; - mOrientedRanges.size.flat = 0; - mOrientedRanges.size.fuzz = 0; - mOrientedRanges.size.resolution = 0; + mOrientedRanges.size = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_SIZE, + .source = mSource, + .min = 0, + .max = 1.0, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; } void TouchInputMapper::initializeOrientedRanges() { // Configure X and Y factors. - mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth(); - mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight(); + mXScale = float(mDisplayBounds.width) / mRawPointerAxes.getRawWidth(); + mYScale = float(mDisplayBounds.height) / mRawPointerAxes.getRawHeight(); mXPrecision = 1.0f / mXScale; mYPrecision = 1.0f / mYScale; @@ -736,21 +702,23 @@ void TouchInputMapper::initializeOrientedRanges() { float pressureMax = 1.0; if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL || mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) { - if (mCalibration.havePressureScale) { - mPressureScale = mCalibration.pressureScale; + if (mCalibration.pressureScale) { + mPressureScale = *mCalibration.pressureScale; pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; } } - mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; - mOrientedRanges.pressure.source = mSource; - mOrientedRanges.pressure.min = 0; - mOrientedRanges.pressure.max = pressureMax; - mOrientedRanges.pressure.flat = 0; - mOrientedRanges.pressure.fuzz = 0; - mOrientedRanges.pressure.resolution = 0; + mOrientedRanges.pressure = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_PRESSURE, + .source = mSource, + .min = 0, + .max = pressureMax, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; // Tilt mTiltXCenter = 0; @@ -771,29 +739,30 @@ void TouchInputMapper::initializeOrientedRanges() { mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution; } - mOrientedRanges.haveTilt = true; - - mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; - mOrientedRanges.tilt.source = mSource; - mOrientedRanges.tilt.min = 0; - mOrientedRanges.tilt.max = M_PI_2; - mOrientedRanges.tilt.flat = 0; - mOrientedRanges.tilt.fuzz = 0; - mOrientedRanges.tilt.resolution = 0; + mOrientedRanges.tilt = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_TILT, + .source = mSource, + .min = 0, + .max = M_PI_2, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; } // Orientation mOrientationScale = 0; if (mHaveTilt) { - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI; - mOrientedRanges.orientation.max = M_PI; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; + mOrientedRanges.orientation = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_ORIENTATION, + .source = mSource, + .min = -M_PI, + .max = M_PI, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; + } else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) { if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::INTERPOLATED) { @@ -808,56 +777,53 @@ void TouchInputMapper::initializeOrientedRanges() { } } - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; + mOrientedRanges.orientation = InputDeviceInfo::MotionRange{ + .axis = AMOTION_EVENT_AXIS_ORIENTATION, + .source = mSource, + .min = -M_PI_2, + .max = M_PI_2, + .flat = 0, + .fuzz = 0, + .resolution = 0, + }; } // Distance mDistanceScale = 0; if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) { if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) { - if (mCalibration.haveDistanceScale) { - mDistanceScale = mCalibration.distanceScale; - } else { - mDistanceScale = 1.0f; - } + mDistanceScale = mCalibration.distanceScale.value_or(1.0f); } - mOrientedRanges.haveDistance = true; + mOrientedRanges.distance = InputDeviceInfo::MotionRange{ - mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; - mOrientedRanges.distance.source = mSource; - mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; - mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; - mOrientedRanges.distance.flat = 0; - mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; - mOrientedRanges.distance.resolution = 0; + .axis = AMOTION_EVENT_AXIS_DISTANCE, + .source = mSource, + .min = mRawPointerAxes.distance.minValue * mDistanceScale, + .max = mRawPointerAxes.distance.maxValue * mDistanceScale, + .flat = 0, + .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale, + .resolution = 0, + }; } // Compute oriented precision, scales and ranges. // Note that the maximum value reported is an inclusive maximum value so it is one // unit less than the total width or height of the display. switch (mInputDeviceOrientation) { - case DISPLAY_ORIENTATION_90: - case DISPLAY_ORIENTATION_270: + case ui::ROTATION_90: + case ui::ROTATION_270: mOrientedXPrecision = mYPrecision; mOrientedYPrecision = mXPrecision; mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayHeight - 1; + mOrientedRanges.x.max = mDisplayBounds.height - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayWidth - 1; + mOrientedRanges.y.max = mDisplayBounds.width - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; @@ -868,13 +834,13 @@ void TouchInputMapper::initializeOrientedRanges() { mOrientedYPrecision = mYPrecision; mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayWidth - 1; + mOrientedRanges.x.max = mDisplayBounds.width - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayHeight - 1; + mOrientedRanges.y.max = mDisplayBounds.height - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; @@ -894,6 +860,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mDeviceMode = DeviceMode::POINTER; if (hasStylus()) { mSource |= AINPUT_SOURCE_STYLUS; + } else { + mSource |= AINPUT_SOURCE_TOUCHPAD; } } else if (isTouchScreen()) { mSource = AINPUT_SOURCE_TOUCHSCREEN; @@ -926,15 +894,19 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) "becomes available.", getDeviceName().c_str()); mDeviceMode = DeviceMode::DISABLED; - } else if (!newViewportOpt->isActive) { + } else if (!mParameters.enableForInactiveViewport && !newViewportOpt->isActive) { ALOGI("Disabling %s (device %i) because the associated viewport is not active", getDeviceName().c_str(), getDeviceId()); mDeviceMode = DeviceMode::DISABLED; } // Raw width and height in the natural orientation. - const int32_t rawWidth = mRawPointerAxes.getRawWidth(); - const int32_t rawHeight = mRawPointerAxes.getRawHeight(); + const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()}; + const int32_t rawXResolution = mRawPointerAxes.x.resolution; + const int32_t rawYResolution = mRawPointerAxes.y.resolution; + // Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0. + const float rawMeanResolution = + (rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0; const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport); const bool viewportChanged = mViewport != newViewport; @@ -945,67 +917,16 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mViewport = newViewport; if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) { - // Convert rotated viewport to the natural orientation. - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; + const auto oldDisplayBounds = mDisplayBounds; // Apply the inverse of the input device orientation so that the input device is // configured in the same orientation as the viewport. The input device orientation will // be re-applied by mInputDeviceOrientation. - const int32_t naturalDeviceOrientation = - (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4; - switch (naturalDeviceOrientation) { - case DISPLAY_ORIENTATION_90: - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; + const ui::Rotation naturalDeviceOrientation = + mViewport.orientation - mParameters.orientation; - const int32_t oldDisplayWidth = mDisplayWidth; - const int32_t oldDisplayHeight = mDisplayHeight; - mDisplayWidth = naturalDeviceWidth; - mDisplayHeight = naturalDeviceHeight; + std::tie(mDisplayBounds, mPhysicalFrameInDisplay) = + getNaturalDisplayInfo(mViewport, naturalDeviceOrientation); // InputReader works in the un-rotated display coordinate space, so we don't need to do // anything if the device is already orientation-aware. If the device is not @@ -1013,28 +934,20 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) // when the display rotation is applied later as a part of the per-window transform, we // get the expected screen coordinates. mInputDeviceOrientation = mParameters.orientationAware - ? DISPLAY_ORIENTATION_0 + ? ui::ROTATION_0 : getInverseRotation(mViewport.orientation); // For orientation-aware devices that work in the un-rotated coordinate space, the // viewport update should be skipped if it is only a change in the orientation. - skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth && - mDisplayHeight == oldDisplayHeight && viewportOrientationChanged; + skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware && + mDisplayBounds == oldDisplayBounds && viewportOrientationChanged; // Apply the input device orientation for the device. - mInputDeviceOrientation = - (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4; + mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation; } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mDisplayWidth = rawWidth; - mDisplayHeight = rawHeight; - mInputDeviceOrientation = DISPLAY_ORIENTATION_0; + mDisplayBounds = rawSize; + mPhysicalFrameInDisplay = Rect{mDisplayBounds}; + mInputDeviceOrientation = ui::ROTATION_0; } - // If displayId changed, do not skip viewport update. - skipViewportUpdate &= !viewportDisplayIdChanged; } // If moving between pointer modes, need to reset some state. @@ -1064,9 +977,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) } if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, " "display id %d", - getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight, + getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(), mInputDeviceOrientation, mDeviceMode, mViewport.displayId); configureVirtualKeys(); @@ -1078,8 +991,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) if (mDeviceMode == DeviceMode::POINTER) { // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight); + float rawDiagonal = hypotf(rawSize.width, rawSize.height); + float displayDiagonal = hypotf(mDisplayBounds.width, mDisplayBounds.height); // Scale movements such that one whole swipe of the touch pad covers a // given area relative to the diagonal size of the display when no acceleration @@ -1097,10 +1010,14 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; mPointerYZoomScale = mPointerXZoomScale; - // Max width between pointers to detect a swipe gesture is more than some fraction - // of the diagonal axis of the touch pad. Touches that are wider than this are - // translated into freeform gestures. - mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; + // Calculate the min freeform gesture width. It will be 0 when the resolution of any + // axis is non positive value. + const float minFreeformGestureWidth = + rawMeanResolution * MIN_FREEFORM_GESTURE_WIDTH_IN_MILLIMETER; + + mPointerGestureMaxSwipeWidth = + std::max(mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal, + minFreeformGestureWidth); } // Inform the dispatcher about the changes. @@ -1111,12 +1028,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) void TouchInputMapper::dumpDisplay(std::string& dump) { dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth); - dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str()); + dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str()); dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation); } @@ -1155,17 +1068,17 @@ void TouchInputMapper::configureVirtualKeys() { int32_t halfWidth = virtualKeyDefinition.width / 2; int32_t halfHeight = virtualKeyDefinition.height / 2; - virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth + + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / + mDisplayBounds.width + touchScreenLeft; - virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth + + virtualKey.hitRight = (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / + mDisplayBounds.width + touchScreenLeft; - virtualKey.hitTop = - (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight + + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / + mDisplayBounds.height + touchScreenTop; - virtualKey.hitBottom = - (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight + + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / + mDisplayBounds.height + touchScreenTop; mVirtualKeys.push_back(virtualKey); } @@ -1191,8 +1104,8 @@ void TouchInputMapper::parseCalibration() { // Size out.sizeCalibration = Calibration::SizeCalibration::DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { + std::string sizeCalibrationString; + if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) { if (sizeCalibrationString == "none") { out.sizeCalibration = Calibration::SizeCalibration::NONE; } else if (sizeCalibrationString == "geometric") { @@ -1204,18 +1117,28 @@ void TouchInputMapper::parseCalibration() { } else if (sizeCalibrationString == "area") { out.sizeCalibration = Calibration::SizeCalibration::AREA; } else if (sizeCalibrationString != "default") { - ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); + ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str()); } } - out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); - out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); - out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); + float sizeScale; + + if (in.tryGetProperty("touch.size.scale", sizeScale)) { + out.sizeScale = sizeScale; + } + float sizeBias; + if (in.tryGetProperty("touch.size.bias", sizeBias)) { + out.sizeBias = sizeBias; + } + bool sizeIsSummed; + if (in.tryGetProperty("touch.size.isSummed", sizeIsSummed)) { + out.sizeIsSummed = sizeIsSummed; + } // Pressure out.pressureCalibration = Calibration::PressureCalibration::DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { + std::string pressureCalibrationString; + if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) { if (pressureCalibrationString == "none") { out.pressureCalibration = Calibration::PressureCalibration::NONE; } else if (pressureCalibrationString == "physical") { @@ -1224,16 +1147,19 @@ void TouchInputMapper::parseCalibration() { out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE; } else if (pressureCalibrationString != "default") { ALOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); + pressureCalibrationString.c_str()); } } - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); + float pressureScale; + if (in.tryGetProperty("touch.pressure.scale", pressureScale)) { + out.pressureScale = pressureScale; + } // Orientation out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { + std::string orientationCalibrationString; + if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) { if (orientationCalibrationString == "none") { out.orientationCalibration = Calibration::OrientationCalibration::NONE; } else if (orientationCalibrationString == "interpolated") { @@ -1242,36 +1168,39 @@ void TouchInputMapper::parseCalibration() { out.orientationCalibration = Calibration::OrientationCalibration::VECTOR; } else if (orientationCalibrationString != "default") { ALOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); + orientationCalibrationString.c_str()); } } // Distance out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT; - String8 distanceCalibrationString; - if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + std::string distanceCalibrationString; + if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) { if (distanceCalibrationString == "none") { out.distanceCalibration = Calibration::DistanceCalibration::NONE; } else if (distanceCalibrationString == "scaled") { out.distanceCalibration = Calibration::DistanceCalibration::SCALED; } else if (distanceCalibrationString != "default") { ALOGW("Invalid value for touch.distance.calibration: '%s'", - distanceCalibrationString.string()); + distanceCalibrationString.c_str()); } } - out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + float distanceScale; + if (in.tryGetProperty("touch.distance.scale", distanceScale)) { + out.distanceScale = distanceScale; + } out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT; - String8 coverageCalibrationString; - if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + std::string coverageCalibrationString; + if (in.tryGetProperty("touch.coverage.calibration", coverageCalibrationString)) { if (coverageCalibrationString == "none") { out.coverageCalibration = Calibration::CoverageCalibration::NONE; } else if (coverageCalibrationString == "box") { out.coverageCalibration = Calibration::CoverageCalibration::BOX; } else if (coverageCalibrationString != "default") { ALOGW("Invalid value for touch.coverage.calibration: '%s'", - coverageCalibrationString.string()); + coverageCalibrationString.c_str()); } } } @@ -1325,17 +1254,17 @@ void TouchInputMapper::dumpCalibration(std::string& dump) { dump += INDENT4 "touch.size.calibration: "; dump += ftl::enum_string(mCalibration.sizeCalibration) + "\n"; - if (mCalibration.haveSizeScale) { - dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); + if (mCalibration.sizeScale) { + dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", *mCalibration.sizeScale); } - if (mCalibration.haveSizeBias) { - dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); + if (mCalibration.sizeBias) { + dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", *mCalibration.sizeBias); } - if (mCalibration.haveSizeIsSummed) { + if (mCalibration.sizeIsSummed) { dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", - toString(mCalibration.sizeIsSummed)); + toString(*mCalibration.sizeIsSummed)); } // Pressure @@ -1353,8 +1282,8 @@ void TouchInputMapper::dumpCalibration(std::string& dump) { ALOG_ASSERT(false); } - if (mCalibration.havePressureScale) { - dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); + if (mCalibration.pressureScale) { + dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", *mCalibration.pressureScale); } // Orientation @@ -1384,8 +1313,8 @@ void TouchInputMapper::dumpCalibration(std::string& dump) { ALOG_ASSERT(false); } - if (mCalibration.haveDistanceScale) { - dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); + if (mCalibration.distanceScale) { + dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", *mCalibration.distanceScale); } switch (mCalibration.coverageCalibration) { @@ -1416,10 +1345,13 @@ void TouchInputMapper::updateAffineTransformation() { mInputDeviceOrientation); } -void TouchInputMapper::reset(nsecs_t when) { +std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) { + std::list<NotifyArgs> out = cancelTouch(when, when); + updateTouchSpots(); + mCursorButtonAccumulator.reset(getDeviceContext()); mCursorScrollAccumulator.reset(getDeviceContext()); - mTouchButtonAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(); mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); @@ -1447,14 +1379,15 @@ void TouchInputMapper::reset(nsecs_t when) { mPointerController->clearSpots(); } - InputMapper::reset(when); + return out += InputMapper::reset(when); } void TouchInputMapper::resetExternalStylus() { mExternalStylusState.clear(); - mExternalStylusId = -1; + mFusedStylusPointerId.reset(); mExternalStylusFusionTimeout = LLONG_MAX; mExternalStylusDataPending = false; + mExternalStylusButtonsApplied = 0; } void TouchInputMapper::clearStylusDataPendingFlags() { @@ -1462,17 +1395,20 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } -void TouchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); + std::list<NotifyArgs> out; if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when, rawEvent->readTime); + out += sync(rawEvent->when, rawEvent->readTime); } + return out; } -void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { +std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; if (mDeviceMode == DeviceMode::DISABLED) { // Only save the last pending state when the device is disabled. mRawStatesPending.clear(); @@ -1501,19 +1437,22 @@ void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { const RawState& last = mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1]; + std::tie(next.when, next.readTime) = + applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when, + readTime, last.when); + // Assign pointer ids. if (!mHavePointerIds) { assignPointerIds(last, next); } - if (DEBUG_RAW_EVENTS) { - ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x", - last.rawPointerData.pointerCount, next.rawPointerData.pointerCount, - last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value, - last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value, - next.rawPointerData.canceledIdBits.value); - } + ALOGD_IF(DEBUG_RAW_EVENTS, + "syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " + "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x", + last.rawPointerData.pointerCount, next.rawPointerData.pointerCount, + last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value, + last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value, + next.rawPointerData.canceledIdBits.value); if (!next.rawPointerData.touchingIdBits.isEmpty() && !next.rawPointerData.hoveringIdBits.isEmpty() && @@ -1522,16 +1461,15 @@ void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { next.rawPointerData.hoveringIdBits.value); } - processRawTouches(false /*timeout*/); + out += processRawTouches(false /*timeout*/); + return out; } -void TouchInputMapper::processRawTouches(bool timeout) { +std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) { + std::list<NotifyArgs> out; if (mDeviceMode == DeviceMode::DISABLED) { - // Drop all input if the device is disabled. - cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime); - mCurrentCookedState.clear(); - updateTouchSpots(); - return; + // Do not process raw event while the device is disabled. + return out; } // Drain any pending touch states. The invariant here is that the mCurrentRawState is always @@ -1551,12 +1489,12 @@ void TouchInputMapper::processRawTouches(bool timeout) { // All ready to go. clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); + mCurrentRawState = next; if (mCurrentRawState.when < mLastRawState.when) { mCurrentRawState.when = mLastRawState.when; mCurrentRawState.readTime = mLastRawState.readTime; } - cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime); + out += cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime); } if (count != 0) { mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); @@ -1566,20 +1504,21 @@ void TouchInputMapper::processRawTouches(bool timeout) { if (timeout) { nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); - if (DEBUG_STYLUS_FUSION) { - ALOGD("Timeout expired, synthesizing event with new stylus data"); - } + mCurrentRawState = mLastRawState; + ALOGD_IF(DEBUG_STYLUS_FUSION, + "Timeout expired, synthesizing event with new stylus data"); const nsecs_t readTime = when; // consider this synthetic event to be zero latency - cookAndDispatch(when, readTime); + out += cookAndDispatch(when, readTime); } else if (mExternalStylusFusionTimeout == LLONG_MAX) { mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); } } + return out; } -void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { +std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; // Always start with a clean state. mCurrentCookedState.clear(); @@ -1605,7 +1544,9 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { // Consume raw off-screen touches before cooking pointer data. // If touches are consumed, subsequent code will not receive any pointer data. - if (consumeRawTouches(when, readTime, policyFlags)) { + bool consumed; + out += consumeRawTouches(when, readTime, policyFlags, consumed /*byref*/); + if (consumed) { mCurrentRawState.rawPointerData.clear(); } @@ -1618,9 +1559,9 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { applyExternalStylusTouchState(when); // Synthesize key down from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(), - mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); + out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(), + mSource, mViewport.displayId, policyFlags, + mLastCookedState.buttonState, mCurrentCookedState.buttonState); // Dispatch the touches either directly or by translation through a pointer on screen. if (mDeviceMode == DeviceMode::POINTER) { @@ -1628,8 +1569,7 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(pointer.toolType)) { mCurrentCookedState.stylusIdBits.markBit(id); } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { @@ -1642,8 +1582,7 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(pointer.toolType)) { mCurrentCookedState.stylusIdBits.markBit(id); } } @@ -1662,15 +1601,15 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { pointerUsage = PointerUsage::GESTURES; } - dispatchPointerUsage(when, readTime, policyFlags, pointerUsage); + out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage); } else { if (!mCurrentMotionAborted) { updateTouchSpots(); - dispatchButtonRelease(when, readTime, policyFlags); - dispatchHoverExit(when, readTime, policyFlags); - dispatchTouches(when, readTime, policyFlags); - dispatchHoverEnterAndMove(when, readTime, policyFlags); - dispatchButtonPress(when, readTime, policyFlags); + out += dispatchButtonRelease(when, readTime, policyFlags); + out += dispatchHoverExit(when, readTime, policyFlags); + out += dispatchTouches(when, readTime, policyFlags); + out += dispatchHoverEnterAndMove(when, readTime, policyFlags); + out += dispatchButtonPress(when, readTime, policyFlags); } if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { @@ -1679,17 +1618,18 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) { } // Synthesize key up from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource, - mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); + out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), + mSource, mViewport.displayId, policyFlags, + mLastCookedState.buttonState, mCurrentCookedState.buttonState); // Clear some transient state. mCurrentRawState.rawVScroll = 0; mCurrentRawState.rawHScroll = 0; // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); + mLastRawState = mCurrentRawState; + mLastCookedState = mCurrentCookedState; + return out; } void TouchInputMapper::updateTouchSpots() { @@ -1708,8 +1648,8 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(), + mCurrentCookedState.cookedPointerData.idToIndex.cbegin(), mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); } @@ -1720,29 +1660,41 @@ bool TouchInputMapper::isTouchScreen() { } void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; + if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) { + // If any of the external buttons are already pressed by the touch device, ignore them. + const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons; + const int32_t releasedButtons = + mExternalStylusButtonsApplied & ~mExternalStylusState.buttons; + + mCurrentRawState.buttonState |= pressedButtons; + mCurrentRawState.buttonState &= ~releasedButtons; + + mExternalStylusButtonsApplied |= pressedButtons; + mExternalStylusButtonsApplied &= ~releasedButtons; } } void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) { + return; + } - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + float pressure = lastPointerData.isTouching(*mFusedStylusPointerId) + ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId) + .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) + : 0.f; + if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) { + pressure = *mExternalStylusState.pressure; + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } + currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId); + properties.toolType = mExternalStylusState.toolType; } } @@ -1751,86 +1703,103 @@ bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeou return false; } + // Check if the stylus pointer has gone up. + if (mFusedStylusPointerId && + !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up"); + mFusedStylusPointerId.reset(); + return false; + } + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { - if (DEBUG_STYLUS_FUSION) { - ALOGD("Have both stylus and touch data, beginning fusion"); - } - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { - if (DEBUG_STYLUS_FUSION) { - ALOGD("Timeout expired, assuming touch is not a stylus."); - } - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } - if (DEBUG_STYLUS_FUSION) { - ALOGD("No stylus data but stylus is connected, requesting timeout " - "(%" PRId64 "ms)", - mExternalStylusFusionTimeout); - } - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } + if (!initialDown) { + return false; } - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { - if (DEBUG_STYLUS_FUSION) { - ALOGD("Stylus pointer is going up"); - } - mExternalStylusId = -1; + if (!mExternalStylusState.pressure) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed"); + return false; + } + + if (*mExternalStylusState.pressure != 0.0f) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion"); + mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + return false; } - return false; + if (timeout) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus."); + mFusedStylusPointerId.reset(); + mExternalStylusFusionTimeout = LLONG_MAX; + return false; + } + + // We are waiting for the external stylus to report a pressure value. Withhold touches from + // being processed until we either get pressure data or timeout. + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } + ALOGD_IF(DEBUG_STYLUS_FUSION, + "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)", + mExternalStylusFusionTimeout); + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; } -void TouchInputMapper::timeoutExpired(nsecs_t when) { +std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { + std::list<NotifyArgs> out; if (mDeviceMode == DeviceMode::POINTER) { if (mPointerUsage == PointerUsage::GESTURES) { // Since this is a synthetic event, we can consider its latency to be zero const nsecs_t readTime = when; - dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/); + out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/); } } else if (mDeviceMode == DeviceMode::DIRECT) { - if (mExternalStylusFusionTimeout < when) { - processRawTouches(true /*timeout*/); + if (mExternalStylusFusionTimeout <= when) { + out += processRawTouches(true /*timeout*/); } else if (mExternalStylusFusionTimeout != LLONG_MAX) { getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); } } + return out; } -void TouchInputMapper::updateExternalStylusState(const StylusState& state) { - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. +std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) { + std::list<NotifyArgs> out; + const bool buttonsChanged = mExternalStylusState.buttons != state.buttons; + mExternalStylusState = state; + if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) { + // The following three cases are handled here: + // - We're in the middle of a fused stream of data; + // - We're waiting on external stylus data before dispatching the initial down; or + // - Only the button state, which is not reported through a specific pointer, has changed. + // Go ahead and dispatch now that we have fresh stylus data. mExternalStylusDataPending = true; - processRawTouches(false /*timeout*/); + out += processRawTouches(false /*timeout*/); } + return out; } -bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, bool& outConsumed) { + outConsumed = false; + std::list<NotifyArgs> out; // Check for release of a virtual key. if (mCurrentVirtualKey.down) { if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { // Pointer went up while virtual key was down. mCurrentVirtualKey.down = false; if (!mCurrentVirtualKey.ignored) { - if (DEBUG_VIRTUAL_KEYS) { - ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); - } - dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + ALOGD_IF(DEBUG_VIRTUAL_KEYS, + "VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); + out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)); } - return true; + outConsumed = true; + return out; } if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { @@ -1840,7 +1809,8 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_ const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { // Pointer is still within the space of the virtual key. - return true; + outConsumed = true; + return out; } } @@ -1850,13 +1820,12 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_ // into the main display surface. mCurrentVirtualKey.down = false; if (!mCurrentVirtualKey.ignored) { - if (DEBUG_VIRTUAL_KEYS) { - ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); - } - dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | - AKEY_EVENT_FLAG_CANCELED); + ALOGD_IF(DEBUG_VIRTUAL_KEYS, "VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); + out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | + AKEY_EVENT_FLAG_CANCELED)); } } @@ -1883,17 +1852,18 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_ virtualKey->scanCode); if (!mCurrentVirtualKey.ignored) { - if (DEBUG_VIRTUAL_KEYS) { - ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); - } - dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | - AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + ALOGD_IF(DEBUG_VIRTUAL_KEYS, + "VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); + out.push_back(dispatchVirtualKey(when, readTime, policyFlags, + AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)); } } } - return true; + outConsumed = true; + return out; } } @@ -1915,43 +1885,80 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_ !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); } - return false; + return out; } -void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { +NotifyKeyArgs TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags) { int32_t keyCode = mCurrentVirtualKey.keyCode; int32_t scanCode = mCurrentVirtualKey.scanCode; nsecs_t downTime = mCurrentVirtualKey.downTime; int32_t metaState = getContext()->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), - AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, - keyEventFlags, keyCode, scanCode, metaState, downTime); - getListener().notifyKey(&args); + return NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, + keyEventFlags, keyCode, scanCode, metaState, downTime); } -void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; if (mCurrentMotionAborted) { // Current motion event was already aborted. - return; + return out; } BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; if (!currentIdBits.isEmpty()) { int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentCookedState.buttonState; - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, + -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); mCurrentMotionAborted = true; } + return out; +} + +// Updates pointer coords and properties for pointers with specified ids that have moved. +// Returns true if any of them changed. +static bool updateMovedPointers(const PropertiesArray& inProperties, CoordsArray& inCoords, + const IdToIndexArray& inIdToIndex, PropertiesArray& outProperties, + CoordsArray& outCoords, IdToIndexArray& outIdToIndex, + BitSet32 idBits) { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; } -void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; int32_t metaState = getContext()->getGlobalMetaState(); @@ -1961,12 +1968,14 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t if (!currentIdBits.isEmpty()) { // No pointer id changes so this is a move event. // The listener takes care of batching moves so we don't have to deal with that here. - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back( + dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, + 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, + -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); } } else { // There may be pointers going up and pointers going down and pointers moving @@ -1996,12 +2005,16 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t if (isCanceled) { ALOGI("Canceling pointer %d for the palm event was detected.", upId); } - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, - isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, + isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, + buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + dispatchedIdBits, upId, mOrientedXPrecision, + mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); dispatchedIdBits.clearBit(upId); mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId); } @@ -2011,12 +2024,14 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t // events, they do not generally handle them except when presented in a move event. if (moveNeeded && !moveIdBits.isEmpty()) { ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, - metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + dispatchedIdBits, -1, mOrientedXPrecision, + mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); } // Dispatch pointer down events using the new pointer locations. @@ -2029,59 +2044,75 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t mDownTime = when; } - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, - 0, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, - downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back( + dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, + 0, mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + dispatchedIdBits, downId, mOrientedXPrecision, + mOrientedYPrecision, mDownTime, MotionClassification::NONE)); } } + return out; } -void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; if (mSentHoverEnter && (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { int32_t metaState = getContext()->getGlobalMetaState(); - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastCookedState.buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, - mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastCookedState.buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); mSentHoverEnter = false; } + return out; } -void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, - uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { int32_t metaState = getContext()->getGlobalMetaState(); if (!mSentHoverEnter) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, - 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, + mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); mSentHoverEnter = true; } - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); } + return out; } -void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); const int32_t metaState = getContext()->getGlobalMetaState(); @@ -2089,16 +2120,21 @@ void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uin while (!releasedButtons.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); buttonState &= ~actionButton; - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); } + return out; } -void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); const int32_t metaState = getContext()->getGlobalMetaState(); @@ -2106,13 +2142,16 @@ void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint3 while (!pressedButtons.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); buttonState |= actionButton; - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, - actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, + buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime, + MotionClassification::NONE)); } + return out; } const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { @@ -2182,7 +2221,7 @@ void TouchInputMapper::cookPointerData() { size = 0; } - if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { + if (mCalibration.sizeIsSummed && *mCalibration.sizeIsSummed) { uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); if (touchingCount > 1) { touchMajor /= touchingCount; @@ -2208,13 +2247,16 @@ void TouchInputMapper::cookPointerData() { toolMinor = toolMajor; } - mCalibration.applySizeScaleAndBias(&touchMajor); - mCalibration.applySizeScaleAndBias(&touchMinor); - mCalibration.applySizeScaleAndBias(&toolMajor); - mCalibration.applySizeScaleAndBias(&toolMinor); + mCalibration.applySizeScaleAndBias(touchMajor); + mCalibration.applySizeScaleAndBias(touchMinor); + mCalibration.applySizeScaleAndBias(toolMajor); + mCalibration.applySizeScaleAndBias(toolMinor); size *= mSizeScale; break; - default: + case Calibration::SizeCalibration::DEFAULT: + LOG_ALWAYS_FATAL("Resolution should not be 'DEFAULT' at this point"); + break; + case Calibration::SizeCalibration::NONE: touchMajor = 0; touchMinor = 0; toolMajor = 0; @@ -2305,40 +2347,37 @@ void TouchInputMapper::cookPointerData() { float left, top, right, bottom; switch (mInputDeviceOrientation) { - case DISPLAY_ORIENTATION_90: + case ui::ROTATION_90: left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale; right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale; bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; orientation -= M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { + if (mOrientedRanges.orientation && orientation < mOrientedRanges.orientation->min) { orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min); } break; - case DISPLAY_ORIENTATION_180: + case ui::ROTATION_180: left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; orientation -= M_PI; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { + if (mOrientedRanges.orientation && orientation < mOrientedRanges.orientation->min) { orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min); } break; - case DISPLAY_ORIENTATION_270: + case ui::ROTATION_270: left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale; top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale; orientation += M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation > mOrientedRanges.orientation.max) { + if (mOrientedRanges.orientation && orientation > mOrientedRanges.orientation->max) { orientation -= - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min); } break; default: @@ -2394,54 +2433,62 @@ void TouchInputMapper::cookPointerData() { } } -void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - PointerUsage pointerUsage) { +std::list<NotifyArgs> TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, + PointerUsage pointerUsage) { + std::list<NotifyArgs> out; if (pointerUsage != mPointerUsage) { - abortPointerUsage(when, readTime, policyFlags); + out += abortPointerUsage(when, readTime, policyFlags); mPointerUsage = pointerUsage; } switch (mPointerUsage) { case PointerUsage::GESTURES: - dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/); + out += dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/); break; case PointerUsage::STYLUS: - dispatchPointerStylus(when, readTime, policyFlags); + out += dispatchPointerStylus(when, readTime, policyFlags); break; case PointerUsage::MOUSE: - dispatchPointerMouse(when, readTime, policyFlags); + out += dispatchPointerMouse(when, readTime, policyFlags); break; case PointerUsage::NONE: break; } + return out; } -void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; switch (mPointerUsage) { case PointerUsage::GESTURES: - abortPointerGestures(when, readTime, policyFlags); + out += abortPointerGestures(when, readTime, policyFlags); break; case PointerUsage::STYLUS: - abortPointerStylus(when, readTime, policyFlags); + out += abortPointerStylus(when, readTime, policyFlags); break; case PointerUsage::MOUSE: - abortPointerMouse(when, readTime, policyFlags); + out += abortPointerMouse(when, readTime, policyFlags); break; case PointerUsage::NONE: break; } mPointerUsage = PointerUsage::NONE; + return out; } -void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - bool isTimeout) { +std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, + bool isTimeout) { + std::list<NotifyArgs> out; // Update current gesture coordinates. bool cancelPreviousGesture, finishPreviousGesture; bool sendEvents = preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); if (!sendEvents) { - return; + return {}; } if (finishPreviousGesture) { cancelPreviousGesture = false; @@ -2455,8 +2502,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, + mPointerController->setSpots(mPointerGesture.currentGestureCoords.cbegin(), + mPointerGesture.currentGestureIdToIndex.cbegin(), mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); } @@ -2498,6 +2545,10 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // Send events! int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentCookedState.buttonState; + const MotionClassification classification = + mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE + ? MotionClassification::TWO_FINGER_SWIPE + : MotionClassification::NONE; uint32_t flags = 0; @@ -2534,11 +2585,15 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); if (!dispatchedGestureIdBits.isEmpty()) { if (cancelPreviousGesture) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, - flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); + const uint32_t cancelFlags = flags | AMOTION_EVENT_FLAG_CANCELED; + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_CANCEL, 0, cancelFlags, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime, classification)); dispatchedGestureIdBits.clear(); } else { @@ -2552,12 +2607,14 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u while (!upGestureIdBits.isEmpty()) { uint32_t id = upGestureIdBits.clearFirstMarkedBit(); - dispatchMotion(when, readTime, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, id, 0, 0, + mPointerGesture.downTime, classification)); dispatchedGestureIdBits.clearBit(id); } @@ -2566,12 +2623,13 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // Send motion events for all pointers that moved. if (moveNeeded) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); + out.push_back( + dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, + flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, + 0, 0, mPointerGesture.downTime, classification)); } // Send motion events for all pointers that went down. @@ -2586,23 +2644,26 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u mPointerGesture.downTime = when; } - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, - 0, flags, metaState, buttonState, 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, flags, metaState, + buttonState, 0, mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + dispatchedGestureIdBits, id, 0, 0, + mPointerGesture.downTime, classification)); } } // Send motion events for hover. if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, + mPointerGesture.downTime, MotionClassification::NONE)); } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { // Synthesize a hover move event after all pointers go up to indicate that // the pointer is hovering again even if the user is not currently touching @@ -2622,12 +2683,13 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState, + buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, x, y, mPointerGesture.downTime, + /* videoFrames */ {})); } // Update state. @@ -2646,18 +2708,28 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u mPointerGesture.lastGestureIdToIndex[id] = index; } } + return out; } -void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + const MotionClassification classification = + mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE + ? MotionClassification::TWO_FINGER_SWIPE + : MotionClassification::NONE; + std::list<NotifyArgs> out; // Cancel previously dispatches pointers. if (!mPointerGesture.lastGestureIdBits.isEmpty()) { int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); + out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, + mPointerGesture.lastGestureIdBits, -1, 0, 0, + mPointerGesture.downTime, classification)); } // Reset the current pointer gesture. @@ -2669,6 +2741,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->clearSpots(); } + return out; } bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, @@ -2678,9 +2751,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Handle TAP timeout. if (isTimeout) { - if (DEBUG_GESTURES) { - ALOGD("Gestures: Processing timeout"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: Processing timeout"); if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) { if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { @@ -2689,9 +2760,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mConfig.pointerGestureTapDragInterval); } else { // The tap is finished. - if (DEBUG_GESTURES) { - ALOGD("Gestures: TAP finished"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP finished"); *outFinishPreviousGesture = true; mPointerGesture.activeGestureId = -1; @@ -2712,17 +2781,18 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - std::vector<VelocityTracker::Position> positions; + std::vector<float> positionsX; + std::vector<float> positionsY; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - float x = pointer.x * mPointerXMovementScale; - float y = pointer.y * mPointerYMovementScale; - positions.push_back({x, y}); + positionsX.push_back(pointer.x * mPointerXMovementScale); + positionsY.push_back(pointer.y * mPointerYMovementScale); } mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - positions); + {{AMOTION_EVENT_AXIS_X, positionsX}, + {AMOTION_EVENT_AXIS_Y, positionsY}}); } // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning @@ -2738,60 +2808,24 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Otherwise choose an arbitrary remaining pointer. // This guarantees we always have an active touch id when there is at least one pointer. // We keep the same active touch id for as long as possible. - int32_t lastActiveTouchId = mPointerGesture.activeTouchId; - int32_t activeTouchId = lastActiveTouchId; - if (activeTouchId < 0) { + if (mPointerGesture.activeTouchId < 0) { if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); + mPointerGesture.activeTouchId = mCurrentCookedState.fingerIdBits.firstMarkedBit(); mPointerGesture.firstTouchTime = when; } - } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = false; - if (activeTouchId < 0) { - mPointerGesture.resetQuietTime(); - } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS || - mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE || - mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) && - currentFingerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == - PointerGesture::Mode::BUTTON_CLICK_OR_DRAG && - currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; - } - } + } else if (!mCurrentCookedState.fingerIdBits.hasBit(mPointerGesture.activeTouchId)) { + mPointerGesture.activeTouchId = !mCurrentCookedState.fingerIdBits.isEmpty() + ? mCurrentCookedState.fingerIdBits.firstMarkedBit() + : -1; } + const int32_t& activeTouchId = mPointerGesture.activeTouchId; // Switch states based on button and pointer state. - if (isQuietTime) { + if (checkForTouchpadQuietTime(when)) { // Case 1: Quiet time. (QUIET) - if (DEBUG_GESTURES) { - ALOGD("Gestures: QUIET for next %0.3fms", - (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * - 0.000001f); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * + 0.000001f); if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) { *outFinishPreviousGesture = true; } @@ -2815,11 +2849,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // active. If the user first puts one finger down to click then adds another // finger to drag then the active pointer should switch to the finger that is // being dragged. - if (DEBUG_GESTURES) { - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " - "currentFingerCount=%d", - activeTouchId, currentFingerCount); - } + ALOGD_IF(DEBUG_GESTURES, + "Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, currentFingerCount=%d", + activeTouchId, currentFingerCount); // Reset state when just starting. if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) { *outFinishPreviousGesture = true; @@ -2829,45 +2861,20 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Switch pointers if needed. // Find the fastest pointer and follow it. if (activeTouchId >= 0 && currentFingerCount > 1) { - int32_t bestId = -1; - float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); - if (speed > bestSpeed) { - bestId = id; - bestSpeed = speed; - } - } - } + const auto [bestId, bestSpeed] = getFastestFinger(); if (bestId >= 0 && bestId != activeTouchId) { - mPointerGesture.activeTouchId = activeTouchId = bestId; - if (DEBUG_GESTURES) { - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " - "bestId=%d, bestSpeed=%0.3f", - bestId, bestSpeed); - } + mPointerGesture.activeTouchId = bestId; + ALOGD_IF(DEBUG_GESTURES, + "Gestures: BUTTON_CLICK_OR_DRAG switched pointers, bestId=%d, " + "bestSpeed=%0.3f", + bestId, bestSpeed); } } - float deltaX = 0, deltaY = 0; if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. // When using spots, the click will occur at the position of the anchor // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); + moveMousePointerFromPointerDelta(when, activeTouchId); } else { mPointerVelocityControl.reset(); } @@ -2903,9 +2910,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { - if (DEBUG_GESTURES) { - ALOGD("Gestures: TAP"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP"); mPointerGesture.tapUpTime = when; getContext()->requestTimeoutAtTime(when + @@ -2931,10 +2936,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi tapped = true; } else { - if (DEBUG_GESTURES) { - ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, - y - mPointerGesture.tapY); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); } } else { if (DEBUG_GESTURES) { @@ -2951,9 +2954,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerVelocityControl.reset(); if (!tapped) { - if (DEBUG_GESTURES) { - ALOGD("Gestures: NEUTRAL"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: NEUTRAL"); mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL; mPointerGesture.currentGestureIdBits.clear(); @@ -2974,50 +2975,30 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; } else { - if (DEBUG_GESTURES) { - ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, y - mPointerGesture.tapY); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); } } else { - if (DEBUG_GESTURES) { - ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", - (when - mPointerGesture.tapUpTime) * 0.000001f); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP_DRAG, %0.3fms time since up", + (when - mPointerGesture.tapUpTime) * 0.000001f); } } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; } - float deltaX = 0, deltaY = 0; if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); + moveMousePointerFromPointerDelta(when, activeTouchId); } else { mPointerVelocityControl.reset(); } bool down; if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) { - if (DEBUG_GESTURES) { - ALOGD("Gestures: TAP_DRAG"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP_DRAG"); down = true; } else { - if (DEBUG_GESTURES) { - ALOGD("Gestures: HOVER"); - } + ALOGD_IF(DEBUG_GESTURES, "Gestures: HOVER"); if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) { *outFinishPreviousGesture = true; } @@ -3048,391 +3029,453 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi } } else { // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) - // We need to provide feedback for each finger that goes down so we cannot wait - // for the fingers to move before deciding what to do. - // - // The ambiguous case is deciding what to do when there are two fingers down but they - // have not moved enough to determine whether they are part of a drag or part of a - // freeform gesture, or just a press or long-press at the pointer location. - // - // When there are two fingers we start with the PRESS hypothesis and we generate a - // down at the pointer location. - // - // When the two fingers move enough or when additional fingers are added, we make - // a decision to transition into SWIPE or FREEFORM mode accordingly. - ALOG_ASSERT(activeTouchId >= 0); + prepareMultiFingerPointerGestures(when, outCancelPreviousGesture, outFinishPreviousGesture); + } - bool settled = when >= - mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; - if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS && - mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE && - mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) { - *outFinishPreviousGesture = true; - } else if (!settled && currentFingerCount > lastFingerCount) { - // Additional pointers have gone down but not yet settled. - // Reset the gesture. - if (DEBUG_GESTURES) { - ALOGD("Gestures: Resetting gesture since additional pointers went down for " - "MULTITOUCH, settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + - mConfig.pointerGestureMultitouchSettleInterval - when) * - 0.000001f); - } - *outCancelPreviousGesture = true; - } else { - // Continue previous gesture. - mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + mPointerController->setButtonState(mCurrentRawState.buttonState); + + if (DEBUG_GESTURES) { + ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + } + return true; +} - if (*outFinishPreviousGesture || *outCancelPreviousGesture) { - mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS; - mPointerGesture.activeGestureId = 0; - mPointerGesture.referenceIdBits.clear(); - mPointerVelocityControl.reset(); +bool TouchInputMapper::checkForTouchpadQuietTime(nsecs_t when) { + if (mPointerGesture.activeTouchId < 0) { + mPointerGesture.resetQuietTime(); + return false; + } + + if (when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval) { + return true; + } + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + bool isQuietTime = false; + if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS || + mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE || + mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) && + currentFingerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG && + currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + return isQuietTime; +} - // Use the centroid and pointer location as the reference points for the gesture. - if (DEBUG_GESTURES) { - ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + - mConfig.pointerGestureMultitouchSettleInterval - when) * - 0.000001f); +std::pair<int32_t, float> TouchInputMapper::getFastestFinger() { + int32_t bestId = -1; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + std::optional<float> vx = + mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id); + std::optional<float> vy = + mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id); + if (vx && vy) { + float speed = hypotf(*vx, *vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; } - mCurrentRawState.rawPointerData - .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); } + } + return std::make_pair(bestId, bestSpeed); +} - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & - ~mPointerGesture.referenceIdBits.value); - !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; +void TouchInputMapper::prepareMultiFingerPointerGestures(nsecs_t when, bool* cancelPreviousGesture, + bool* finishPreviousGesture) { + // We need to provide feedback for each finger that goes down so we cannot wait for the fingers + // to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they have not + // moved enough to determine whether they are part of a drag or part of a freeform gesture, or + // just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a down at the + // pointer location. + // + // When the two fingers move enough or when additional fingers are added, we make a decision to + // transition into SWIPE or FREEFORM mode accordingly. + const int32_t activeTouchId = mPointerGesture.activeTouchId; + ALOG_ASSERT(activeTouchId >= 0); + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); + bool settled = + when >= mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; + if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS && + mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE && + mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) { + *finishPreviousGesture = true; + } else if (!settled && currentFingerCount > lastFingerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: Resetting gesture since additional pointers went down for " + "MULTITOUCH, settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); + *cancelPreviousGesture = true; + } else { + // Continue previous gesture. + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (*finishPreviousGesture || *cancelPreviousGesture) { + mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS; + mPointerGesture.activeGestureId = 0; + mPointerGesture.referenceIdBits.clear(); + mPointerVelocityControl.reset(); + + // Use the centroid and pointer location as the reference points for the gesture. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining " + "%0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); + mCurrentRawState.rawPointerData + .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & + ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + + // Add delta for all fingers and calculate a common movement delta. + int32_t commonDeltaRawX = 0, commonDeltaRawY = 0; + BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); + const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaRawX = delta.dx; + commonDeltaRawY = delta.dy; + } else { + commonDeltaRawX = calculateCommonVector(commonDeltaRawX, delta.dx); + commonDeltaRawY = calculateCommonVector(commonDeltaRawY, delta.dy); } - mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + } - // Add delta for all fingers and calculate a common movement delta. - float commonDeltaX = 0, commonDeltaY = 0; - BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value); - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { - bool first = (idBits == commonIdBits); + // Consider transitions from PRESS to SWIPE or MULTITOUCH. + if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) { + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); - const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); + dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; } } - // Consider transitions from PRESS to SWIPE or MULTITOUCH. - if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) { - float dist[MAX_POINTER_ID + 1]; - int32_t distOverThreshold = 0; - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); - if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { - distOverThreshold += 1; - } - } - - // Only transition when at least two pointers have moved further than - // the minimum distance threshold. - if (distOverThreshold >= 2) { - if (currentFingerCount > 2) { - // There are more than two pointers, switch to FREEFORM. - if (DEBUG_GESTURES) { - ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); - } - *outCancelPreviousGesture = true; + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + if (currentFingerCount > 2) { + // There are more than two pointers, switch to FREEFORM. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); + *cancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; + } else { + // There are exactly two pointers. + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + uint32_t id1 = idBits.clearFirstMarkedBit(); + uint32_t id2 = idBits.firstMarkedBit(); + const RawPointerData::Pointer& p1 = + mCurrentRawState.rawPointerData.pointerForId(id1); + const RawPointerData::Pointer& p2 = + mCurrentRawState.rawPointerData.pointerForId(id2); + float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); + if (mutualDistance > mPointerGestureMaxSwipeWidth) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + mutualDistance, mPointerGestureMaxSwipeWidth); + *cancelPreviousGesture = true; mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; } else { - // There are exactly two pointers. - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - uint32_t id1 = idBits.clearFirstMarkedBit(); - uint32_t id2 = idBits.firstMarkedBit(); - const RawPointerData::Pointer& p1 = - mCurrentRawState.rawPointerData.pointerForId(id1); - const RawPointerData::Pointer& p2 = - mCurrentRawState.rawPointerData.pointerForId(id2); - float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); - if (mutualDistance > mPointerGestureMaxSwipeWidth) { - // There are two pointers but they are too far apart for a SWIPE, - // switch to FREEFORM. - if (DEBUG_GESTURES) { - ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > " - "%0.3f", - mutualDistance, mPointerGestureMaxSwipeWidth); - } - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - float dist1 = dist[id1]; - float dist2 = dist[id2]; - if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && - dist2 >= mConfig.pointerGestureMultitouchMinDistance) { - // Calculate the dot product of the displacement vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * - // mag(v2). - PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; - PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; - float dx1 = delta1.dx * mPointerXZoomScale; - float dy1 = delta1.dy * mPointerYZoomScale; - float dx2 = delta2.dx * mPointerXZoomScale; - float dy2 = delta2.dy * mPointerYZoomScale; - float dot = dx1 * dx2 + dy1 * dy2; - float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. - if (DEBUG_GESTURES) { - ALOGD("Gestures: PRESS transitioned to SWIPE, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); - } - mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. - if (DEBUG_GESTURES) { - ALOGD("Gestures: PRESS transitioned to FREEFORM, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); - } - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; - } + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && + dist2 >= mConfig.pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approaches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * + // mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dx1 = delta1.dx * mPointerXZoomScale; + float dy1 = delta1.dy * mPointerYZoomScale; + float dx2 = delta2.dx * mPointerXZoomScale; + float dy2 = delta2.dy * mPointerYZoomScale; + float dot = dx1 * dx2 + dy1 * dy2; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); + mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); + *cancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; } } } } - } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) { - // Switch from SWIPE to FREEFORM if additional pointers go down. - // Cancel previous gesture. - if (currentFingerCount > 2) { - if (DEBUG_GESTURES) { - ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); - } - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; - } } + } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (currentFingerCount > 2) { + ALOGD_IF(DEBUG_GESTURES, + "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); + *cancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM; + } + } - // Move the reference points based on the overall group motion of the fingers - // except in PRESS mode while waiting for a transition to occur. - if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS && - (commonDeltaX || commonDeltaY)) { - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS && + (commonDeltaRawX || commonDeltaRawY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx = 0; + delta.dy = 0; + } - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; + mPointerGesture.referenceTouchX += commonDeltaRawX; + mPointerGesture.referenceTouchY += commonDeltaRawY; - commonDeltaX *= mPointerXMovementScale; - commonDeltaY *= mPointerYMovementScale; + float commonDeltaX = commonDeltaRawX * mPointerXMovementScale; + float commonDeltaY = commonDeltaRawY * mPointerYMovementScale; - rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY); - mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY); + mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - } + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; + } - // Report gestures. - if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS || - mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) { - // PRESS or SWIPE mode. - if (DEBUG_GESTURES) { - ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); - } - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) { + // PRESS or SWIPE mode. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, " + "currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - // FREEFORM mode. - if (DEBUG_GESTURES) { - ALOGD("Gestures: FREEFORM activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); - } - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) { + float xOffset = static_cast<float>(commonDeltaRawX) / + (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue); + float yOffset = static_cast<float>(commonDeltaRawY) / + (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset); + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { + // FREEFORM mode. + ALOGD_IF(DEBUG_GESTURES, + "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, " + "currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.clear(); - BitSet32 mappedTouchIdBits; - BitSet32 usedGestureIdBits; - if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) { - // Initially, assign the active gesture id to the active touch point - // if there is one. No other touch id bits are mapped yet. - if (!*outCancelPreviousGesture) { - mappedTouchIdBits.markBit(activeTouchId); - usedGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = - mPointerGesture.activeGestureId; - } else { - mPointerGesture.activeGestureId = -1; - } + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*cancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; } else { - // Otherwise, assume we mapped all touches from the previous frame. - // Reuse all mappings that are still applicable. - mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value; - usedGestureIdBits = mPointerGesture.lastGestureIdBits; - - // Check whether we need to choose a new active gesture id because the - // current went went up. - for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & - ~mCurrentCookedState.fingerIdBits.value); - !upTouchIdBits.isEmpty();) { - uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); - uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; - if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { - mPointerGesture.activeGestureId = -1; - break; - } - } - } - - if (DEBUG_GESTURES) { - ALOGD("Gestures: FREEFORM follow up " - "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " - "activeGestureId=%d", - mappedTouchIdBits.value, usedGestureIdBits.value, - mPointerGesture.activeGestureId); + mPointerGesture.activeGestureId = -1; } - - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - for (uint32_t i = 0; i < currentFingerCount; i++) { - uint32_t touchId = idBits.clearFirstMarkedBit(); - uint32_t gestureId; - if (!mappedTouchIdBits.hasBit(touchId)) { - gestureId = usedGestureIdBits.markFirstUnmarkedBit(); - mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; - if (DEBUG_GESTURES) { - ALOGD("Gestures: FREEFORM " - "new mapping for touch id %d -> gesture id %d", - touchId, gestureId); - } - } else { - gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; - if (DEBUG_GESTURES) { - ALOGD("Gestures: FREEFORM " - "existing mapping for touch id %d -> gesture id %d", - touchId, gestureId); - } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = + mLastCookedState.fingerIdBits.value & mCurrentCookedState.fingerIdBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & + ~mCurrentCookedState.fingerIdBits.value); + !upTouchIdBits.isEmpty();) { + uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; } - mPointerGesture.currentGestureIdBits.markBit(gestureId); - mPointerGesture.currentGestureIdToIndex[gestureId] = i; - - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(touchId); - float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; - float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; - rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); - - mPointerGesture.currentGestureProperties[i].clear(); - mPointerGesture.currentGestureProperties[i].id = gestureId; - mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[i].clear(); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX + deltaX); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY + deltaY); - mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - 1.0f); } + } - if (mPointerGesture.activeGestureId < 0) { - mPointerGesture.activeGestureId = - mPointerGesture.currentGestureIdBits.firstMarkedBit(); - if (DEBUG_GESTURES) { - ALOGD("Gestures: FREEFORM new activeGestureId=%d", - mPointerGesture.activeGestureId); - } + ALOGD_IF(DEBUG_GESTURES, + "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, mPointerGesture.activeGestureId); + + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + for (uint32_t i = 0; i < currentFingerCount; i++) { + uint32_t touchId = idBits.clearFirstMarkedBit(); + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.markFirstUnmarkedBit(); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; + ALOGD_IF(DEBUG_GESTURES, + "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d", touchId, + gestureId); + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; + ALOGD_IF(DEBUG_GESTURES, + "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); } - } - } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; - mPointerController->setButtonState(mCurrentRawState.buttonState); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(touchId); + float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; + float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); - if (DEBUG_GESTURES) { - ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGestureIdBits=0x%08x", - toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); - for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; - ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + mPointerGesture.currentGestureProperties[i].clear(); + mPointerGesture.currentGestureProperties[i].id = gestureId; + mPointerGesture.currentGestureProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX + + deltaX); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY + + deltaY); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); } - for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; - ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = mPointerGesture.currentGestureIdBits.firstMarkedBit(); + ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d", + mPointerGesture.activeGestureId); } } - return true; } -void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +void TouchInputMapper::moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(pointerId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(pointerId); + float deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + float deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + mPointerController->move(deltaX, deltaY); +} + +std::list<NotifyArgs> TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { mPointerSimple.currentCoords.clear(); mPointerSimple.currentProperties.clear(); @@ -3461,35 +3504,24 @@ void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uin hovering = false; } - dispatchPointerSimple(when, readTime, policyFlags, down, hovering); + return dispatchPointerSimple(when, readTime, policyFlags, down, hovering); } -void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { - abortPointerSimple(when, readTime, policyFlags); +std::list<NotifyArgs> TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + return abortPointerSimple(when, readTime, policyFlags); } -void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { +std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { mPointerSimple.currentCoords.clear(); mPointerSimple.currentProperties.clear(); bool down, hovering; if (!mCurrentCookedState.mouseIdBits.isEmpty()) { uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); - uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - float deltaX = 0, deltaY = 0; if (mLastCookedState.mouseIdBits.hasBit(id)) { - uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - - mLastRawState.rawPointerData.pointers[lastIndex].x) * - mPointerXMovementScale; - deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - - mLastRawState.rawPointerData.pointers[lastIndex].y) * - mPointerYMovementScale; - - rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - mPointerController->move(deltaX, deltaY); + moveMousePointerFromPointerDelta(when, id); } else { mPointerVelocityControl.reset(); } @@ -3499,6 +3531,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint float x, y; mPointerController->getPosition(&x, &y); + uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3515,19 +3548,24 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint hovering = false; } - dispatchPointerSimple(when, readTime, policyFlags, down, hovering); + return dispatchPointerSimple(when, readTime, policyFlags, down, hovering); } -void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { - abortPointerSimple(when, readTime, policyFlags); +std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out = abortPointerSimple(when, readTime, policyFlags); mPointerVelocityControl.reset(); + + return out; } -void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - bool down, bool hovering) { +std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, bool down, + bool hovering) { LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER, "%s cannot be used when the device is not in POINTER mode.", __func__); + std::list<NotifyArgs> out; int32_t metaState = getContext()->getGlobalMetaState(); if (down || hovering) { @@ -3547,28 +3585,29 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, + 0, metaState, mLastRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {})); } if (mPointerSimple.hovering && !hovering) { mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {})); } if (down) { @@ -3577,25 +3616,26 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {})); } // Send move. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, + 0, 0, metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {})); } if (hovering) { @@ -3603,25 +3643,26 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {})); } // Send hover move. - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back( + NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {})); } if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { @@ -3636,14 +3677,14 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, + 0, 0, metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &pointerCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {})); } // Save state. @@ -3657,38 +3698,38 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin } else { mPointerSimple.reset(); } + return out; } -void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - +std::list<NotifyArgs> TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags) { + std::list<NotifyArgs> out; if (mPointerSimple.down || mPointerSimple.hovering) { int32_t metaState = getContext()->getGlobalMetaState(); - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), - mPointerSimple.source, mPointerSimple.displayId, policyFlags, - AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, - metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.lastCursorX, mPointerSimple.lastCursorY, - mPointerSimple.downTime, - /* videoFrames */ {}); - getListener().notifyMotion(&args); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mPointerSimple.source, mPointerSimple.displayId, policyFlags, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, mLastRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, + mPointerSimple.lastCursorX, mPointerSimple.lastCursorY, + mPointerSimple.downTime, + /* videoFrames */ {})); if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); } } mPointerSimple.reset(); + return out; } -void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - uint32_t source, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, - BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime) { +NotifyMotionArgs TouchInputMapper::dispatchMotion( + nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, + const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime, MotionClassification classification) { PointerCoords pointerCoords[MAX_POINTERS]; PointerProperties pointerProperties[MAX_POINTERS]; uint32_t pointerCount = 0; @@ -3734,47 +3775,18 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); }); - NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, - pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, - downTime, std::move(frames)); - getListener().notifyMotion(&args); -} - -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, - const uint32_t* inIdToIndex, - PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; + return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + classification, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); } -void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) { - abortPointerUsage(when, readTime, 0 /*policyFlags*/); - abortTouches(when, readTime, 0 /* policyFlags*/); +std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; + out += abortPointerUsage(when, readTime, 0 /*policyFlags*/); + out += abortTouches(when, readTime, 0 /* policyFlags*/); + return out; } // Transform input device coordinates to display panel coordinates. @@ -3791,19 +3803,19 @@ void TouchInputMapper::rotateAndScale(float& x, float& y) const { // 180 - reverse x, y. // 270 - swap x/y and reverse x. switch (mInputDeviceOrientation) { - case DISPLAY_ORIENTATION_0: + case ui::ROTATION_0: x = xScaled; y = yScaled; break; - case DISPLAY_ORIENTATION_90: + case ui::ROTATION_90: y = xScaledMax; x = yScaled; break; - case DISPLAY_ORIENTATION_180: + case ui::ROTATION_180: x = xScaledMax; y = yScaledMax; break; - case DISPLAY_ORIENTATION_270: + case ui::ROTATION_270: y = xScaled; x = yScaledMax; break; @@ -3817,19 +3829,17 @@ bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const { const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale; return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight); + isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled); } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { for (const VirtualKey& virtualKey : mVirtualKeys) { - if (DEBUG_VIRTUAL_KEYS) { - ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, - virtualKey.hitTop, virtualKey.hitRight, virtualKey.hitBottom); - } + ALOGD_IF(DEBUG_VIRTUAL_KEYS, + "VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, + virtualKey.hitTop, virtualKey.hitRight, virtualKey.hitBottom); if (virtualKey.isHit(x, y)) { return &virtualKey; @@ -4000,11 +4010,10 @@ void TouchInputMapper::assignPointerIds(const RawState& last, RawState& current) currentPointerIndex)); usedIdBits.markBit(id); - if (DEBUG_POINTER_ASSIGNMENT) { - ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 - ", distance=%" PRIu64, - lastPointerIndex, currentPointerIndex, id, heap[0].distance); - } + ALOGD_IF(DEBUG_POINTER_ASSIGNMENT, + "assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 + ", distance=%" PRIu64, + lastPointerIndex, currentPointerIndex, id, heap[0].distance); break; } } @@ -4019,10 +4028,9 @@ void TouchInputMapper::assignPointerIds(const RawState& last, RawState& current) current.rawPointerData.markIdBit(id, current.rawPointerData.isHovering(currentPointerIndex)); - if (DEBUG_POINTER_ASSIGNMENT) { - ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, - id); - } + ALOGD_IF(DEBUG_POINTER_ASSIGNMENT, + "assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, + id); } } @@ -4054,10 +4062,11 @@ int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode return AKEY_STATE_UNKNOWN; } -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, + const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) { for (const VirtualKey& virtualKey : mVirtualKeys) { - for (size_t i = 0; i < numCodes; i++) { + for (size_t i = 0; i < keyCodes.size(); i++) { if (virtualKey.keyCode == keyCodes[i]) { outFlags[i] = 1; } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 2937bf88d4..34ba62515f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H +#pragma once #include <stdint.h> +#include <ui/Rotation.h> #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" @@ -28,55 +28,65 @@ namespace android { +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + /* Raw axis information from the driver. */ struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); + RawAbsoluteAxisInfo x{}; + RawAbsoluteAxisInfo y{}; + RawAbsoluteAxisInfo pressure{}; + RawAbsoluteAxisInfo touchMajor{}; + RawAbsoluteAxisInfo touchMinor{}; + RawAbsoluteAxisInfo toolMajor{}; + RawAbsoluteAxisInfo toolMinor{}; + RawAbsoluteAxisInfo orientation{}; + RawAbsoluteAxisInfo distance{}; + RawAbsoluteAxisInfo tiltX{}; + RawAbsoluteAxisInfo tiltY{}; + RawAbsoluteAxisInfo trackingId{}; + RawAbsoluteAxisInfo slot{}; + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); + inline void clear() { *this = RawPointerAxes(); } }; +using PropertiesArray = std::array<PointerProperties, MAX_POINTERS>; +using CoordsArray = std::array<PointerCoords, MAX_POINTERS>; +using IdToIndexArray = std::array<uint32_t, MAX_POINTER_ID + 1>; + /* Raw data for a collection of pointers including a pointer id mapping table. */ struct RawPointerData { struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; + uint32_t id{0xFFFFFFFF}; + int32_t x{}; + int32_t y{}; + int32_t pressure{}; + int32_t touchMajor{}; + int32_t touchMinor{}; + int32_t toolMajor{}; + int32_t toolMinor{}; + int32_t orientation{}; + int32_t distance{}; + int32_t tiltX{}; + int32_t tiltY{}; + // A fully decoded AMOTION_EVENT_TOOL_TYPE constant. + int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN}; + bool isHovering{false}; }; - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t pointerCount{}; + std::array<Pointer, MAX_POINTERS> pointers{}; + BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}; + IdToIndexArray idToIndex{}; + + inline void clear() { *this = RawPointerData(); } - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); void getCentroidOfTouchingPointers(float* outX, float* outY) const; inline void markIdBit(uint32_t id, bool isHovering) { @@ -100,15 +110,13 @@ struct RawPointerData { /* Cooked data for a collection of pointers including a pointer id mapping table. */ struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t pointerCount{}; + PropertiesArray pointerProperties{}; + CoordsArray pointerCoords{}; + BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}, validIdBits{}; + IdToIndexArray idToIndex{}; - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); + inline void clear() { *this = CookedPointerData(); } inline const PointerCoords& pointerCoordsForId(uint32_t id) const { return pointerCoords[idToIndex[id]]; @@ -141,18 +149,21 @@ public: uint32_t getSources() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; - void reset(nsecs_t when) override; - void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) override; - void cancelTouch(nsecs_t when, nsecs_t readTime) override; - void timeoutExpired(nsecs_t when) override; - void updateExternalStylusState(const StylusState& state) override; + [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) override; + [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState( + const StylusState& state) override; std::optional<int32_t> getAssociatedDisplayId() override; protected: @@ -197,7 +208,6 @@ protected: struct Parameters { enum class DeviceType { TOUCH_SCREEN, - TOUCH_PAD, TOUCH_NAVIGATION, POINTER, @@ -209,15 +219,7 @@ protected: bool associatedDisplayIsExternal; bool orientationAware; - enum class Orientation : int32_t { - ORIENTATION_0 = DISPLAY_ORIENTATION_0, - ORIENTATION_90 = DISPLAY_ORIENTATION_90, - ORIENTATION_180 = DISPLAY_ORIENTATION_180, - ORIENTATION_270 = DISPLAY_ORIENTATION_270, - - ftl_last = ORIENTATION_270 - }; - Orientation orientation; + ui::Rotation orientation; bool hasButtonUnderPad; std::string uniqueDisplayId; @@ -231,6 +233,12 @@ protected: GestureMode gestureMode; bool wake; + + // Whether the device supports the Universal Stylus Initiative (USI) protocol for styluses. + bool supportsUsi; + + // Allows touches while the display is off. + bool enableForInactiveViewport; } mParameters; // Immutable calibration parameters in parsed form. @@ -248,12 +256,9 @@ protected: SizeCalibration sizeCalibration; - bool haveSizeScale; - float sizeScale; - bool haveSizeBias; - float sizeBias; - bool haveSizeIsSummed; - bool sizeIsSummed; + std::optional<float> sizeScale; + std::optional<float> sizeBias; + std::optional<bool> sizeIsSummed; // Pressure enum class PressureCalibration { @@ -264,8 +269,7 @@ protected: }; PressureCalibration pressureCalibration; - bool havePressureScale; - float pressureScale; + std::optional<float> pressureScale; // Orientation enum class OrientationCalibration { @@ -285,8 +289,7 @@ protected: }; DistanceCalibration distanceCalibration; - bool haveDistanceScale; - float distanceScale; + std::optional<float> distanceScale; enum class CoverageCalibration { DEFAULT, @@ -296,15 +299,15 @@ protected: CoverageCalibration coverageCalibration; - inline void applySizeScaleAndBias(float* outSize) const { - if (haveSizeScale) { - *outSize *= sizeScale; + inline void applySizeScaleAndBias(float& outSize) const { + if (sizeScale) { + outSize *= *sizeScale; } - if (haveSizeBias) { - *outSize += sizeBias; + if (sizeBias) { + outSize += *sizeBias; } - if (*outSize < 0) { - *outSize = 0; + if (outSize < 0) { + outSize = 0; } } } mCalibration; @@ -315,65 +318,33 @@ protected: RawPointerAxes mRawPointerAxes; struct RawState { - nsecs_t when; - nsecs_t readTime; + nsecs_t when{std::numeric_limits<nsecs_t>::min()}; + nsecs_t readTime{}; // Raw pointer sample data. - RawPointerData rawPointerData; + RawPointerData rawPointerData{}; - int32_t buttonState; + int32_t buttonState{}; // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - explicit inline RawState() { clear(); } - - void copyFrom(const RawState& other) { - when = other.when; - readTime = other.readTime; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } + int32_t rawVScroll{}; + int32_t rawHScroll{}; - void clear() { - when = 0; - readTime = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } + inline void clear() { *this = RawState(); } }; struct CookedState { // Cooked pointer sample data. - CookedPointerData cookedPointerData; + CookedPointerData cookedPointerData{}; // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } + BitSet32 fingerIdBits{}; + BitSet32 stylusIdBits{}; + BitSet32 mouseIdBits{}; - void clear() { - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } + int32_t buttonState{}; + + inline void clear() { *this = CookedState(); } }; std::vector<RawState> mRawStatesPending; @@ -384,9 +355,14 @@ protected: // State provided by an external stylus StylusState mExternalStylusState; - int64_t mExternalStylusId; + // If an external stylus is capable of reporting pointer-specific data like pressure, we will + // attempt to fuse the pointer data reported by the stylus to the first touch pointer. This is + // the id of the pointer to which the external stylus data is fused. + std::optional<uint32_t> mFusedStylusPointerId; nsecs_t mExternalStylusFusionTimeout; bool mExternalStylusDataPending; + // A subset of the buttons in mCurrentRawState that came from an external stylus. + int32_t mExternalStylusButtonsApplied; // True if we sent a HOVER_ENTER event. bool mSentHoverEnter; @@ -429,22 +405,19 @@ private: // The components of the viewport are specified in the display's rotated orientation. DisplayViewport mViewport; - // The width and height are obtained from the viewport and are specified - // in the natural orientation. - int32_t mDisplayWidth; - int32_t mDisplayHeight; + // We refer to the display as being in the "natural orientation" when there is no rotation + // applied. The display size obtained from the viewport in the natural orientation. + // Always starts at (0, 0). + ui::Size mDisplayBounds{ui::kInvalidSize}; - // The physical frame is the rectangle in the display's coordinate space that maps to the + // The physical frame is the rectangle in the natural display's coordinate space that maps to // the logical display frame. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; + Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT}; // The orientation of the input device relative to that of the display panel. It specifies // the rotation of the input device coordinates required to produce the display panel // orientation, so it will depend on whether the device is orientation aware. - int32_t mInputDeviceOrientation; + ui::Rotation mInputDeviceOrientation; // Translation and scaling factors, orientation-independent. float mXScale; @@ -477,35 +450,29 @@ private: InputDeviceInfo::MotionRange y; InputDeviceInfo::MotionRange pressure; - bool haveSize; - InputDeviceInfo::MotionRange size; - - bool haveTouchSize; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; + std::optional<InputDeviceInfo::MotionRange> size; - bool haveToolSize; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; + std::optional<InputDeviceInfo::MotionRange> touchMajor; + std::optional<InputDeviceInfo::MotionRange> touchMinor; - bool haveOrientation; - InputDeviceInfo::MotionRange orientation; + std::optional<InputDeviceInfo::MotionRange> toolMajor; + std::optional<InputDeviceInfo::MotionRange> toolMinor; - bool haveDistance; - InputDeviceInfo::MotionRange distance; + std::optional<InputDeviceInfo::MotionRange> orientation; - bool haveTilt; - InputDeviceInfo::MotionRange tilt; + std::optional<InputDeviceInfo::MotionRange> distance; - OrientedRanges() { clear(); } + std::optional<InputDeviceInfo::MotionRange> tilt; void clear() { - haveSize = false; - haveTouchSize = false; - haveToolSize = false; - haveOrientation = false; - haveDistance = false; - haveTilt = false; + size = std::nullopt; + touchMajor = std::nullopt; + touchMinor = std::nullopt; + toolMajor = std::nullopt; + toolMinor = std::nullopt; + orientation = std::nullopt; + distance = std::nullopt; + tilt = std::nullopt; } } mOrientedRanges; @@ -529,13 +496,15 @@ private: float mPointerXZoomScale; float mPointerYZoomScale; - // The maximum swipe width. + // The maximum swipe width between pointers to detect a swipe gesture + // in the number of pixels.Touches that are wider than this are translated + // into freeform gestures. float mPointerGestureMaxSwipeWidth; struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance + uint32_t currentPointerIndex : 8 {}; + uint32_t lastPointerIndex : 8 {}; + uint64_t distance : 48 {}; // squared distance }; enum class PointerUsage { @@ -632,15 +601,15 @@ private: // Pointer coords and ids for the current and previous pointer gesture. Mode currentGestureMode; BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; + IdToIndexArray currentGestureIdToIndex{}; + PropertiesArray currentGestureProperties{}; + CoordsArray currentGestureCoords{}; Mode lastGestureMode; BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; + IdToIndexArray lastGestureIdToIndex{}; + PropertiesArray lastGestureProperties{}; + CoordsArray lastGestureCoords{}; // Time the pointer gesture last went down. nsecs_t downTime; @@ -749,43 +718,75 @@ private: void initializeOrientedRanges(); void initializeSizeRanges(); - void sync(nsecs_t when, nsecs_t readTime); - - bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void processRawTouches(bool timeout); - void cookAndDispatch(nsecs_t when, nsecs_t readTime); - void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags); - - void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime); + + [[nodiscard]] std::list<NotifyArgs> consumeRawTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, bool& outConsumed); + [[nodiscard]] std::list<NotifyArgs> processRawTouches(bool timeout); + [[nodiscard]] std::list<NotifyArgs> cookAndDispatch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] NotifyKeyArgs dispatchVirtualKey(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags); + + [[nodiscard]] std::list<NotifyArgs> dispatchTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchHoverExit(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchButtonRelease(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchButtonPress(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); void cookPointerData(); - void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - - void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - PointerUsage pointerUsage); - void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - - void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, - bool isTimeout); - void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> abortTouches(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + + [[nodiscard]] std::list<NotifyArgs> dispatchPointerUsage(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, + PointerUsage pointerUsage); + [[nodiscard]] std::list<NotifyArgs> abortPointerUsage(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + + [[nodiscard]] std::list<NotifyArgs> dispatchPointerGestures(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, + bool isTimeout); + [[nodiscard]] std::list<NotifyArgs> abortPointerGestures(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout); - void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + // Returns true if we're in a period of "quiet time" when touchpad gestures should be ignored. + bool checkForTouchpadQuietTime(nsecs_t when); + + std::pair<int32_t, float> getFastestFinger(); + + void prepareMultiFingerPointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture); - void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); - void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + // Moves the on-screen mouse pointer based on the movement of the pointer of the given ID + // between the last and current events. Uses a relative motion. + void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId); - void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down, - bool hovering); - void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchPointerStylus(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> abortPointerStylus(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> dispatchPointerMouse(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + [[nodiscard]] std::list<NotifyArgs> abortPointerMouse(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + + [[nodiscard]] std::list<NotifyArgs> dispatchPointerSimple(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags, bool down, + bool hovering); + [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime, + uint32_t policyFlags); + + // Attempts to assign a pointer id to the external stylus. Returns true if the state should be + // withheld from further processing while waiting for data from the stylus. bool assignExternalStylusId(const RawState& state, bool timeout); void applyExternalStylusButtonState(nsecs_t when); void applyExternalStylusTouchState(nsecs_t when); @@ -794,18 +795,12 @@ private: // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the // method will take care of setting the index and transmuting the action to DOWN or UP // it is the first / last pointer to go down / up. - void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, - int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); - - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, - const uint32_t* inIdToIndex, PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const; + [[nodiscard]] NotifyMotionArgs dispatchMotion( + nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, + const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime, MotionClassification classification); // Returns if this touch device is a touch screen with an associated display. bool isTouchScreen(); @@ -818,10 +813,7 @@ private: static void assignPointerIds(const RawState& last, RawState& current); - const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; }; } // namespace android - -#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp new file mode 100644 index 0000000000..de6e4b0193 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -0,0 +1,365 @@ +/* + * Copyright 2022 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 "../Macros.h" + +#include <chrono> + +#include <android/input.h> +#include <log/log_main.h> +#include "TouchCursorInputMapperCommon.h" +#include "TouchpadInputMapper.h" + +namespace android { + +namespace { + +short getMaxTouchCount(const InputDeviceContext& context) { + if (context.hasKeyCode(BTN_TOOL_QUINTTAP)) return 5; + if (context.hasKeyCode(BTN_TOOL_QUADTAP)) return 4; + if (context.hasKeyCode(BTN_TOOL_TRIPLETAP)) return 3; + if (context.hasKeyCode(BTN_TOOL_DOUBLETAP)) return 2; + if (context.hasKeyCode(BTN_TOOL_FINGER)) return 1; + return 0; +} + +HardwareProperties createHardwareProperties(const InputDeviceContext& context) { + HardwareProperties props; + RawAbsoluteAxisInfo absMtPositionX; + context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX); + props.left = absMtPositionX.minValue; + props.right = absMtPositionX.maxValue; + props.res_x = absMtPositionX.resolution; + + RawAbsoluteAxisInfo absMtPositionY; + context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY); + props.top = absMtPositionY.minValue; + props.bottom = absMtPositionY.maxValue; + props.res_y = absMtPositionY.resolution; + + RawAbsoluteAxisInfo absMtOrientation; + context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation); + props.orientation_minimum = absMtOrientation.minValue; + props.orientation_maximum = absMtOrientation.maxValue; + + RawAbsoluteAxisInfo absMtSlot; + context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot); + props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1; + props.max_touch_cnt = getMaxTouchCount(context); + + // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5 + // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads + // that did this, so assume false. + props.supports_t5r2 = false; + + props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT); + props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD); + + // Mouse-only properties, which will always be false. + props.has_wheel = false; + props.wheel_is_hi_res = false; + + // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads + // are haptic. + props.is_haptic_pad = false; + return props; +} + +void gestureInterpreterCallback(void* clientData, const Gesture* gesture) { + TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData); + mapper->consumeGesture(gesture); +} + +uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { + switch (gesturesButton) { + case GESTURES_BUTTON_LEFT: + return AMOTION_EVENT_BUTTON_PRIMARY; + case GESTURES_BUTTON_MIDDLE: + return AMOTION_EVENT_BUTTON_TERTIARY; + case GESTURES_BUTTON_RIGHT: + return AMOTION_EVENT_BUTTON_SECONDARY; + case GESTURES_BUTTON_BACK: + return AMOTION_EVENT_BUTTON_BACK; + case GESTURES_BUTTON_FORWARD: + return AMOTION_EVENT_BUTTON_FORWARD; + default: + return 0; + } +} + +} // namespace + +TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext), + mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter), + mPointerController(getContext()->getPointerController(getDeviceId())), + mTouchButtonAccumulator(deviceContext) { + mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD); + mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext)); + // Even though we don't explicitly delete copy/move semantics, it's safe to + // give away a pointer to TouchpadInputMapper here because + // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and + // 2) TouchpadInputMapper is stored as a unique_ptr and not moved. + mGestureInterpreter->SetCallback(gestureInterpreterCallback, this); + // TODO(b/251196347): set a property provider, so we can change gesture properties. + // TODO(b/251196347): set a timer provider, so the library can use timers. + + RawAbsoluteAxisInfo slotAxisInfo; + getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo); + if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) { + ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work " + "properly.", + getDeviceName().c_str()); + } + mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true); + mTouchButtonAccumulator.configure(); +} + +TouchpadInputMapper::~TouchpadInputMapper() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } +} + +uint32_t TouchpadInputMapper::getSources() const { + return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD; +} + +std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) { + mCursorButtonAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(); + mMscTimestamp = 0; + + mButtonState = 0; + return InputMapper::reset(when); +} + +std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) { + std::list<NotifyArgs> out = {}; + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + out = sync(rawEvent->when, rawEvent->readTime); + } + if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { + mMscTimestamp = rawEvent->value; + } + mCursorButtonAccumulator.process(rawEvent); + mMotionAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + return out; +} + +std::list<NotifyArgs> TouchpadInputMapper::sync(nsecs_t when, nsecs_t readTime) { + HardwareState hwState; + // The gestures library uses doubles to represent timestamps in seconds. + hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count(); + hwState.msc_timestamp = + std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count(); + + hwState.buttons_down = 0; + if (mCursorButtonAccumulator.isLeftPressed()) { + hwState.buttons_down |= GESTURES_BUTTON_LEFT; + } + if (mCursorButtonAccumulator.isMiddlePressed()) { + hwState.buttons_down |= GESTURES_BUTTON_MIDDLE; + } + if (mCursorButtonAccumulator.isRightPressed()) { + hwState.buttons_down |= GESTURES_BUTTON_RIGHT; + } + if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) { + hwState.buttons_down |= GESTURES_BUTTON_BACK; + } + if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) { + hwState.buttons_down |= GESTURES_BUTTON_FORWARD; + } + + std::vector<FingerState> fingers; + for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) { + MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i); + if (slot.isInUse()) { + FingerState& fingerState = fingers.emplace_back(); + fingerState = {}; + fingerState.touch_major = slot.getTouchMajor(); + fingerState.touch_minor = slot.getTouchMinor(); + fingerState.width_major = slot.getToolMajor(); + fingerState.width_minor = slot.getToolMinor(); + fingerState.pressure = slot.getPressure(); + fingerState.orientation = slot.getOrientation(); + fingerState.position_x = slot.getX(); + fingerState.position_y = slot.getY(); + fingerState.tracking_id = slot.getTrackingId(); + } + } + hwState.fingers = fingers.data(); + hwState.finger_cnt = fingers.size(); + hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount(); + + mProcessing = true; + mGestureInterpreter->PushHardwareState(&hwState); + mProcessing = false; + + std::list<NotifyArgs> out = processGestures(when, readTime); + + mMotionAccumulator.finishSync(); + mMscTimestamp = 0; + return out; +} + +void TouchpadInputMapper::consumeGesture(const Gesture* gesture) { + ALOGD("Gesture ready: %s", gesture->String().c_str()); + if (!mProcessing) { + ALOGE("Received gesture outside of the normal processing flow; ignoring it."); + return; + } + mGesturesToProcess.push_back(*gesture); +} + +std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out = {}; + for (Gesture& gesture : mGesturesToProcess) { + switch (gesture.type) { + case kGestureTypeMove: + out.push_back(handleMove(when, readTime, gesture)); + break; + case kGestureTypeButtonsChange: + out += handleButtonsChange(when, readTime, gesture); + break; + default: + // TODO(b/251196347): handle more gesture types. + break; + } + } + mGesturesToProcess.clear(); + return out; +} + +NotifyArgs TouchpadInputMapper::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { + PointerProperties props; + props.clear(); + props.id = 0; + props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); + mPointerController->move(gesture.details.move.dx, gesture.details.move.dy); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy); + const bool down = isPointerDown(mButtonState); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); + + const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; + return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, + /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition); +} + +std::list<NotifyArgs> TouchpadInputMapper::handleButtonsChange(nsecs_t when, nsecs_t readTime, + const Gesture& gesture) { + std::list<NotifyArgs> out = {}; + + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + + PointerProperties props; + props.clear(); + props.id = 0; + props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + const uint32_t buttonsPressed = gesture.details.buttons.down; + bool pointerDown = isPointerDown(mButtonState) || + buttonsPressed & + (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f); + + uint32_t newButtonState = mButtonState; + std::list<NotifyArgs> pressEvents = {}; + for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) { + if (buttonsPressed & button) { + uint32_t actionButton = gesturesButtonToMotionEventButton(button); + newButtonState |= actionButton; + pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, + actionButton, newButtonState, + /* pointerCount= */ 1, &props, &coords, + xCursorPosition, yCursorPosition)); + } + } + if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) { + mDownTime = when; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1, + &props, &coords, xCursorPosition, yCursorPosition)); + } + out.splice(out.end(), pressEvents); + + // The same button may be in both down and up in the same gesture, in which case we should treat + // it as having gone down and then up. So, we treat a single button change gesture as two state + // changes: a set of buttons going down, followed by a set of buttons going up. + mButtonState = newButtonState; + + const uint32_t buttonsReleased = gesture.details.buttons.up; + for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) { + if (buttonsReleased & button) { + uint32_t actionButton = gesturesButtonToMotionEventButton(button); + newButtonState &= ~actionButton; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + actionButton, newButtonState, /* pointerCount= */ 1, + &props, &coords, xCursorPosition, yCursorPosition)); + } + } + if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) { + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, + newButtonState, /* pointerCount= */ 1, &props, &coords, + xCursorPosition, yCursorPosition)); + } + mButtonState = newButtonState; + return out; +} + +NotifyMotionArgs TouchpadInputMapper::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, + int32_t actionButton, int32_t buttonState, + uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, + float xCursorPosition, float yCursorPosition) { + // TODO(b/260226362): consider what the appropriate source for these events is. + const uint32_t source = AINPUT_SOURCE_MOUSE; + + return NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), source, + mPointerController->getDisplayId(), /* policyFlags= */ 0, action, + /* actionButton= */ actionButton, /* flags= */ 0, + getContext()->getGlobalMetaState(), buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, + /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition, + yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {}); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h new file mode 100644 index 0000000000..fe6b1fe759 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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 <memory> + +#include <PointerControllerInterface.h> + +#include "EventHub.h" +#include "InputDevice.h" +#include "InputMapper.h" +#include "NotifyArgs.h" +#include "accumulator/CursorButtonAccumulator.h" +#include "accumulator/MultiTouchMotionAccumulator.h" +#include "accumulator/TouchButtonAccumulator.h" + +#include "include/gestures.h" + +namespace android { + +class TouchpadInputMapper : public InputMapper { +public: + explicit TouchpadInputMapper(InputDeviceContext& deviceContext); + ~TouchpadInputMapper(); + + uint32_t getSources() const override; + [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + + void consumeGesture(const Gesture* gesture); + +private: + [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime); + NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime, + const Gesture& gesture); + + NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, + int32_t actionButton, int32_t buttonState, + uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xCursorPosition, + float yCursorPosition); + + std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)> + mGestureInterpreter; + std::shared_ptr<PointerControllerInterface> mPointerController; + + CursorButtonAccumulator mCursorButtonAccumulator; + MultiTouchMotionAccumulator mMotionAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + int32_t mMscTimestamp = 0; + + bool mProcessing = false; + std::vector<Gesture> mGesturesToProcess; + + // The current button state according to the gestures library, but converted into MotionEvent + // button values (AMOTION_EVENT_BUTTON_...). + uint32_t mButtonState = 0; + nsecs_t mDownTime = 0; +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 33db527db1..7645b12f1b 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -35,16 +35,18 @@ void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { info->setVibrator(true); } -void VibratorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) { // TODO: Handle FF_STATUS, although it does not seem to be widely supported. + return {}; } -void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, - int32_t token) { +std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence, + ssize_t repeat, int32_t token) { if (DEBUG_VIBRATOR) { ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), sequence.toString().c_str(), repeat, token); } + std::list<NotifyArgs> out; mVibrating = true; mSequence = sequence; @@ -53,19 +55,22 @@ void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t rep mIndex = -1; // Request InputReader to notify InputManagerService for vibration started. - NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true); - getListener().notifyVibratorState(&args); - nextStep(); + out.push_back( + NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), true)); + out += nextStep(); + return out; } -void VibratorInputMapper::cancelVibrate(int32_t token) { +std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) { if (DEBUG_VIBRATOR) { ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); } + std::list<NotifyArgs> out; if (mVibrating && mToken == token) { - stopVibrating(); + out.push_back(stopVibrating()); } + return out; } bool VibratorInputMapper::isVibrating() { @@ -76,26 +81,29 @@ std::vector<int32_t> VibratorInputMapper::getVibratorIds() { return getDeviceContext().getVibratorIds(); } -void VibratorInputMapper::timeoutExpired(nsecs_t when) { +std::list<NotifyArgs> VibratorInputMapper::timeoutExpired(nsecs_t when) { + std::list<NotifyArgs> out; if (mVibrating) { if (when >= mNextStepTime) { - nextStep(); + out += nextStep(); } else { getContext()->requestTimeoutAtTime(mNextStepTime); } } + return out; } -void VibratorInputMapper::nextStep() { +std::list<NotifyArgs> VibratorInputMapper::nextStep() { if (DEBUG_VIBRATOR) { ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId()); } + std::list<NotifyArgs> out; mIndex += 1; if (size_t(mIndex) >= mSequence.pattern.size()) { if (mRepeat < 0) { // We are done. - stopVibrating(); - return; + out.push_back(stopVibrating()); + return out; } mIndex = mRepeat; } @@ -122,9 +130,10 @@ void VibratorInputMapper::nextStep() { if (DEBUG_VIBRATOR) { ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count()); } + return out; } -void VibratorInputMapper::stopVibrating() { +NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() { mVibrating = false; if (DEBUG_VIBRATOR) { ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); @@ -132,8 +141,7 @@ void VibratorInputMapper::stopVibrating() { getDeviceContext().cancelVibrate(); // Request InputReader to notify InputManagerService for vibration complete. - NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false); - getListener().notifyVibratorState(&args); + return NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), false); } void VibratorInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index d3c22b60a8..e98f63a8b4 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H -#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H +#pragma once #include "InputMapper.h" @@ -28,13 +27,14 @@ public: virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; - virtual void process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; - virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override; - virtual void cancelVibrate(int32_t token) override; + [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat, + int32_t token) override; + [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token) override; virtual bool isVibrating() override; virtual std::vector<int32_t> getVibratorIds() override; - virtual void timeoutExpired(nsecs_t when) override; + [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override; virtual void dump(std::string& dump) override; private: @@ -45,10 +45,8 @@ private: ssize_t mIndex; nsecs_t mNextStepTime; - void nextStep(); - void stopVibrating(); + [[nodiscard]] std::list<NotifyArgs> nextStep(); + [[nodiscard]] NotifyVibratorStateArgs stopVibrating(); }; } // namespace android - -#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h index 9e159064fa..138060483d 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H -#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H +#pragma once #include <stdint.h> @@ -33,6 +32,14 @@ public: void process(const RawEvent* rawEvent); uint32_t getButtonState() const; + inline bool isLeftPressed() const { return mBtnLeft; } + inline bool isRightPressed() const { return mBtnRight; } + inline bool isMiddlePressed() const { return mBtnMiddle; } + inline bool isBackPressed() const { return mBtnBack; } + inline bool isSidePressed() const { return mBtnSide; } + inline bool isForwardPressed() const { return mBtnForward; } + inline bool isExtraPressed() const { return mBtnExtra; } + inline bool isTaskPressed() const { return mBtnTask; } private: bool mBtnLeft; @@ -48,5 +55,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h index 16495599d7..ae1b7a32f4 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H -#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H +#pragma once #include <stdint.h> @@ -56,5 +55,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp new file mode 100644 index 0000000000..2da1d814fa --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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 "HidUsageAccumulator.h" + +namespace android { + +void HidUsageAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) { + mCurrentHidUsage = rawEvent.value; + return; + } + + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + reset(); + return; + } +} + +int32_t HidUsageAccumulator::consumeCurrentHidUsage() { + const int32_t currentHidUsage = mCurrentHidUsage; + reset(); + return currentHidUsage; +} + +} // namespace android diff --git a/services/surfaceflinger/ContainerLayer.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h index 9b7bab1e1d..740a710483 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2022 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. @@ -13,29 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #pragma once -#include <sys/types.h> +#include "EventHub.h" #include <cstdint> -#include "Layer.h" - namespace android { -class ContainerLayer : public Layer { +/* Keeps track of the state of currently reported HID usage code. */ +class HidUsageAccumulator { public: - explicit ContainerLayer(const LayerCreationArgs&); - ~ContainerLayer() override; + explicit HidUsageAccumulator() = default; + inline void reset() { *this = HidUsageAccumulator(); } - const char* getType() const override { return "ContainerLayer"; } - bool isVisible() const override; + void process(const RawEvent& rawEvent); - bool isCreatedFromMainThread() const override { return true; } + /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */ + int32_t consumeCurrentHidUsage(); -protected: - bool canDrawShadows() const override { return false; } - sp<Layer> createClone() override; +private: + int32_t mCurrentHidUsage{}; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..8746729425 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2022 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. + */ + +// clang-format off +#include "../Macros.h" +// clang-format on +#include "MultiTouchMotionAccumulator.h" + +namespace android { + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), mUsingSlotsProtocol(false) {} + +void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, + bool usingSlotsProtocol) { + mUsingSlotsProtocol = usingSlotsProtocol; + mSlots = std::vector<Slot>(slotCount); + + mCurrentSlot = -1; + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot before we + // start reading events from the device. It is possible that the current slot index will + // not be the same as it was when the first event was written into the evdev buffer, which + // means the input mapper could start out of sync with the initial state of the events in + // the evdev buffer. In the extremely unlikely case that this happens, the data from two + // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the + // touch point to "jump", but at least there will be no stuck touches. + int32_t initialSlot; + if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); + status == OK) { + mCurrentSlot = initialSlot; + } else { + ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); + } + } +} + +void MultiTouchMotionAccumulator::resetSlots() { + for (Slot& slot : mSlots) { + slot.clear(); + } + mCurrentSlot = -1; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { + if (newSlot) { + ALOGW_IF(DEBUG_POINTERS, + "MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlots.size() - 1); + } + } else { + Slot& slot = mSlots[mCurrentSlot]; + // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of + // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while + // updating the slot. + if (!mUsingSlotsProtocol) { + slot.mInUse = true; + } + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot.mAbsMtPositionX = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_POSITION_Y: + slot.mAbsMtPositionY = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_TOUCH_MAJOR: + slot.mAbsMtTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot.mAbsMtTouchMinor = rawEvent->value; + slot.mHaveAbsMtTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot.mAbsMtWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot.mAbsMtWidthMinor = rawEvent->value; + slot.mHaveAbsMtWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot.mAbsMtOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot.mInUse = false; + } else { + slot.mInUse = true; + slot.mAbsMtTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot.mAbsMtPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot.mAbsMtDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot.mAbsMtToolType = rawEvent->value; + slot.mHaveAbsMtToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + resetSlots(); + } +} + +void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { + if (!slot.mInUse) { + ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", + event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId); + } +} + +// --- MultiTouchMotionAccumulator::Slot --- + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMtToolType) { + switch (mAbsMtToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + case MT_TOOL_PALM: + return AMOTION_EVENT_TOOL_TYPE_PALM; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h new file mode 100644 index 0000000000..62bc780df5 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 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 <linux/input-event-codes.h> +#include <stdint.h> +#include <vector> + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMtPositionX; } + inline int32_t getY() const { return mAbsMtPositionY; } + inline int32_t getTouchMajor() const { return mAbsMtTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMtTouchMinor ? mAbsMtTouchMinor : mAbsMtTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMtWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMtWidthMinor ? mAbsMtWidthMinor : mAbsMtWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMtOrientation; } + inline int32_t getTrackingId() const { return mAbsMtTrackingId; } + inline int32_t getPressure() const { return mAbsMtPressure; } + inline int32_t getDistance() const { return mAbsMtDistance; } + int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse = false; + bool mHaveAbsMtTouchMinor = false; + bool mHaveAbsMtWidthMinor = false; + bool mHaveAbsMtToolType = false; + + int32_t mAbsMtPositionX = 0; + int32_t mAbsMtPositionY = 0; + int32_t mAbsMtTouchMajor = 0; + int32_t mAbsMtTouchMinor = 0; + int32_t mAbsMtWidthMajor = 0; + int32_t mAbsMtWidthMinor = 0; + int32_t mAbsMtOrientation = 0; + int32_t mAbsMtTrackingId = -1; + int32_t mAbsMtPressure = 0; + int32_t mAbsMtDistance = 0; + int32_t mAbsMtToolType = 0; + + void clear() { *this = Slot(); } + }; + + MultiTouchMotionAccumulator(); + + void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); + void process(const RawEvent* rawEvent); + void finishSync(); + + inline size_t getSlotCount() const { return mSlots.size(); } + inline const Slot& getSlot(size_t index) const { + LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); + return mSlots[index]; + } + +private: + int32_t mCurrentSlot; + std::vector<Slot> mSlots; + bool mUsingSlotsProtocol; + + void resetSlots(); + void warnIfNotInUse(const RawEvent& event, const Slot& slot); +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h index 4c011f16a7..93056f06e6 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H -#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H +#pragma once #include <stdint.h> @@ -53,5 +52,3 @@ private: }; } // namespace android - -#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index 86153d3f5e..66017024eb 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -21,55 +21,40 @@ namespace android { -TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); +void TouchButtonAccumulator::configure() { + mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH); + mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) || + mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) || + mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) || + mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) || + mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH); } -void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) { - mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH); - mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) || - deviceContext.hasScanCode(BTN_TOOL_RUBBER) || - deviceContext.hasScanCode(BTN_TOOL_BRUSH) || - deviceContext.hasScanCode(BTN_TOOL_PENCIL) || - deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) { - mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH); - mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS); +void TouchButtonAccumulator::reset() { + mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH); + mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) || + mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY); // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0); - mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; + mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) || + mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY); + mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP); + mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP); + mHidUsageAccumulator.reset(); } void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + mHidUsageAccumulator.process(*rawEvent); + if (rawEvent->type == EV_KEY) { switch (rawEvent->code) { case BTN_TOUCH: @@ -116,7 +101,32 @@ void TouchButtonAccumulator::process(const RawEvent* rawEvent) { case BTN_TOOL_QUADTAP: mBtnToolQuadTap = rawEvent->value; break; + case BTN_TOOL_QUINTTAP: + mBtnToolQuintTap = rawEvent->value; + break; + default: + processMappedKey(rawEvent->code, rawEvent->value); } + return; + } +} + +void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) { + int32_t keyCode, metaState; + uint32_t flags; + if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(), + 0 /*metaState*/, &keyCode, &metaState, &flags) != OK) { + return; + } + switch (keyCode) { + case AKEYCODE_STYLUS_BUTTON_PRIMARY: + mBtnStylus = down; + break; + case AKEYCODE_STYLUS_BUTTON_SECONDARY: + mBtnStylus2 = down; + break; + default: + break; } } @@ -141,7 +151,8 @@ int32_t TouchButtonAccumulator::getToolType() const { if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { return AMOTION_EVENT_TOOL_TYPE_STYLUS; } - if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { + if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || + mBtnToolQuintTap) { return AMOTION_EVENT_TOOL_TYPE_FINGER; } return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; @@ -150,7 +161,7 @@ int32_t TouchButtonAccumulator::getToolType() const { bool TouchButtonAccumulator::isToolActive() const { return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || - mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; + mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap; } bool TouchButtonAccumulator::isHovering() const { @@ -161,4 +172,19 @@ bool TouchButtonAccumulator::hasStylus() const { return mHaveStylus; } +bool TouchButtonAccumulator::hasButtonTouch() const { + return mHaveBtnTouch; +} + +int TouchButtonAccumulator::getTouchCount() const { + if (mBtnTouch) { + if (mBtnToolQuintTap) return 5; + if (mBtnToolQuadTap) return 4; + if (mBtnToolTripleTap) return 3; + if (mBtnToolDoubleTap) return 2; + if (mBtnToolFinger) return 1; + } + return 0; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index 22ebb720d5..2e70e2e50d 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H -#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H +#pragma once -#include <stdint.h> +#include <cstdint> +#include "HidUsageAccumulator.h" namespace android { @@ -27,9 +27,11 @@ struct RawEvent; /* Keeps track of the state of touch, stylus and tool buttons. */ class TouchButtonAccumulator { public: - TouchButtonAccumulator(); - void configure(InputDeviceContext& deviceContext); - void reset(InputDeviceContext& deviceContext); + explicit TouchButtonAccumulator(InputDeviceContext& deviceContext) + : mDeviceContext(deviceContext){}; + + void configure(); + void reset(); void process(const RawEvent* rawEvent); @@ -38,29 +40,34 @@ public: bool isToolActive() const; bool isHovering() const; bool hasStylus() const; + bool hasButtonTouch() const; + int getTouchCount() const; private: - bool mHaveBtnTouch; - bool mHaveStylus; + bool mHaveBtnTouch{}; + bool mHaveStylus{}; + + bool mBtnTouch{}; + bool mBtnStylus{}; + bool mBtnStylus2{}; + bool mBtnToolFinger{}; + bool mBtnToolPen{}; + bool mBtnToolRubber{}; + bool mBtnToolBrush{}; + bool mBtnToolPencil{}; + bool mBtnToolAirbrush{}; + bool mBtnToolMouse{}; + bool mBtnToolLens{}; + bool mBtnToolDoubleTap{}; + bool mBtnToolTripleTap{}; + bool mBtnToolQuadTap{}; + bool mBtnToolQuintTap{}; - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; + HidUsageAccumulator mHidUsageAccumulator{}; - void clearButtons(); + InputDeviceContext& mDeviceContext; + + void processMappedKey(int32_t scanCode, bool down); }; } // namespace android - -#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp new file mode 100644 index 0000000000..81b4968df8 --- /dev/null +++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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 LOG_TAG +#define LOG_TAG "Gestures" +#endif + +#include <stdio.h> + +#include <log/log.h> + +#include "include/gestures.h" + +extern "C" { + +void gestures_log(int verb, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + if (verb == GESTURES_LOG_ERROR) { + LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args); + } else if (verb == GESTURES_LOG_INFO) { + LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args); + } else { + LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args); + } + va_end(args); +} +} diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index 74307310c5..693ff063b1 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -23,13 +23,14 @@ package { cc_library_headers { name: "libinputreporter_headers", + host_supported: true, export_include_dirs: ["."], } filegroup { name: "libinputreporter_sources", srcs: [ - "InputReporter.cpp", + "InputReporter.cpp", ], } diff --git a/services/inputflinger/reporter/InputReporter.cpp b/services/inputflinger/reporter/InputReporter.cpp index b591d3f909..6f1eef653c 100644 --- a/services/inputflinger/reporter/InputReporter.cpp +++ b/services/inputflinger/reporter/InputReporter.cpp @@ -35,7 +35,7 @@ void InputReporter::reportDroppedKey(uint32_t sequenceNum) { } sp<InputReporterInterface> createInputReporter() { - return new InputReporter(); + return sp<InputReporter>::make(); } } // namespace android diff --git a/services/inputflinger/reporter/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h index e5d360609f..72a4aeb13b 100644 --- a/services/inputflinger/reporter/InputReporterInterface.h +++ b/services/inputflinger/reporter/InputReporterInterface.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_INPUT_REPORTER_INTERFACE_H -#define _UI_INPUT_REPORTER_INTERFACE_H +#pragma once #include <utils/RefBase.h> @@ -49,5 +48,3 @@ public: sp<InputReporterInterface> createInputReporter(); } // namespace android - -#endif // _UI_INPUT_REPORTER_INTERFACE_H diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 75cd9da782..53d821f197 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -23,6 +23,7 @@ package { cc_test { name: "inputflinger_tests", + host_supported: true, defaults: [ "inputflinger_defaults", // For all targets inside inputflinger, these tests build all of their sources using their @@ -39,14 +40,18 @@ cc_test { "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "EventHub_test.cpp", + "FakeEventHub.cpp", + "FakeInputReaderPolicy.cpp", + "FakePointerController.cpp", "FocusResolver_test.cpp", - "IInputFlingerQuery.aidl", - "InputClassifier_test.cpp", - "InputClassifierConverter_test.cpp", + "InputMapperTest.cpp", + "InputProcessor_test.cpp", + "InputProcessorConverter_test.cpp", "InputDispatcher_test.cpp", "InputReader_test.cpp", - "InputFlingerService_test.cpp", + "InstrumentedInputReader.cpp", "LatencyTracker_test.cpp", + "NotifyArgs_test.cpp", "PreferStylusOverTouch_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", @@ -58,10 +63,33 @@ cc_test { "frameworks/native/libs/input", ], }, + target: { + android: { + shared_libs: [ + "libinput", + "libvintf", + ], + }, + host: { + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi", + ], + cflags: [ + "-D__ANDROID_HOST__", + ], + static_libs: [ + "libinput", + ], + }, + }, static_libs: [ "libc++fs", "libgmock", ], require_root: true, + test_options: { + unit_test: true, + }, test_suites: ["device-tests"], } diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp index b561da107d..25adeea48f 100644 --- a/services/inputflinger/tests/AnrTracker_test.cpp +++ b/services/inputflinger/tests/AnrTracker_test.cpp @@ -40,8 +40,8 @@ TEST(AnrTrackerTest, SingleEntry_First) { TEST(AnrTrackerTest, MultipleEntries_RemoveToken) { AnrTracker tracker; - sp<IBinder> token1 = new BBinder(); - sp<IBinder> token2 = new BBinder(); + sp<IBinder> token1 = sp<BBinder>::make(); + sp<IBinder> token2 = sp<BBinder>::make(); tracker.insert(1, token1); tracker.insert(2, token2); @@ -90,8 +90,8 @@ TEST(AnrTrackerTest, SingleToken_MaintainsOrder) { TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) { AnrTracker tracker; - sp<IBinder> token1 = new BBinder(); - sp<IBinder> token2 = new BBinder(); + sp<IBinder> token1 = sp<BBinder>::make(); + sp<IBinder> token2 = sp<BBinder>::make(); tracker.insert(2, token1); tracker.insert(5, token2); @@ -104,8 +104,8 @@ TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) { TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) { AnrTracker tracker; - sp<IBinder> token1 = new BBinder(); - sp<IBinder> token2 = new BBinder(); + sp<IBinder> token1 = sp<BBinder>::make(); + sp<IBinder> token2 = sp<BBinder>::make(); tracker.insert(2, token1); tracker.insert(2, token2); @@ -119,8 +119,8 @@ TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) { TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) { AnrTracker tracker; - sp<IBinder> token1 = new BBinder(); - sp<IBinder> token2 = new BBinder(); + sp<IBinder> token1 = sp<BBinder>::make(); + sp<IBinder> token2 = sp<BBinder>::make(); tracker.insert(2, token1); tracker.insert(2, token2); @@ -137,7 +137,7 @@ TEST(AnrTrackerTest, Empty_DoesntCrash) { ASSERT_TRUE(tracker.empty()); - ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout()); + ASSERT_EQ(LLONG_MAX, tracker.firstTimeout()); // Can't call firstToken() if tracker.empty() } @@ -152,12 +152,12 @@ TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) { ASSERT_EQ(nullptr, tracker.firstToken()); // Remove with non-matching token - tracker.erase(1, new BBinder()); + tracker.erase(1, sp<BBinder>::make()); ASSERT_EQ(1, tracker.firstTimeout()); ASSERT_EQ(nullptr, tracker.firstToken()); // Remove with both non-matching - tracker.erase(2, new BBinder()); + tracker.erase(2, sp<BBinder>::make()); ASSERT_EQ(1, tracker.firstTimeout()); ASSERT_EQ(nullptr, tracker.firstToken()); } diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index 6ef6e4498c..2e296daa22 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -68,12 +68,18 @@ protected: int32_t mDeviceId; virtual void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP() << "It's only possible to interact with uinput on device"; +#endif mEventHub = std::make_unique<EventHub>(); consumeInitialDeviceAddedEvents(); mKeyboard = createUinputDevice<UinputHomeKey>(); ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation()); } virtual void TearDown() override { +#if !defined(__ANDROID__) + return; +#endif mKeyboard.reset(); waitForDeviceClose(mDeviceId); assertNoMoreEvents(); @@ -99,8 +105,6 @@ protected: }; std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) { - static constexpr size_t EVENT_BUFFER_SIZE = 256; - std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer; std::vector<RawEvent> events; while (true) { @@ -108,12 +112,12 @@ std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEven if (expectedEvents) { timeout = 2s; } - const size_t count = - mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size()); - if (count == 0) { + + std::vector<RawEvent> newEvents = mEventHub->getEvents(timeout.count()); + if (newEvents.empty()) { break; } - events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count); + events.insert(events.end(), newEvents.begin(), newEvents.end()); if (expectedEvents && events.size() >= *expectedEvents) { break; } diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp new file mode 100644 index 0000000000..289a7809cc --- /dev/null +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -0,0 +1,596 @@ +/* + * Copyright 2022 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 "FakeEventHub.h" + +#include <android-base/thread_annotations.h> +#include <gtest/gtest.h> +#include <linux/input-event-codes.h> + +#include "TestConstants.h" + +namespace android { + +const std::string FakeEventHub::BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery"; + +FakeEventHub::~FakeEventHub() { + for (size_t i = 0; i < mDevices.size(); i++) { + delete mDevices.valueAt(i); + } +} + +void FakeEventHub::addDevice(int32_t deviceId, const std::string& name, + ftl::Flags<InputDeviceClass> classes, int bus) { + Device* device = new Device(classes); + device->identifier.name = name; + device->identifier.bus = bus; + mDevices.add(deviceId, device); + + enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0); +} + +void FakeEventHub::removeDevice(int32_t deviceId) { + delete mDevices.valueFor(deviceId); + mDevices.removeItem(deviceId); + + enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0); +} + +bool FakeEventHub::isDeviceEnabled(int32_t deviceId) const { + Device* device = getDevice(deviceId); + if (device == nullptr) { + ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); + return false; + } + return device->enabled; +} + +status_t FakeEventHub::enableDevice(int32_t deviceId) { + status_t result; + Device* device = getDevice(deviceId); + if (device == nullptr) { + ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); + return BAD_VALUE; + } + if (device->enabled) { + ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId); + return OK; + } + result = device->enable(); + return result; +} + +status_t FakeEventHub::disableDevice(int32_t deviceId) { + Device* device = getDevice(deviceId); + if (device == nullptr) { + ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); + return BAD_VALUE; + } + if (!device->enabled) { + ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId); + return OK; + } + return device->disable(); +} + +void FakeEventHub::finishDeviceScan() { + enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0); +} + +void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) { + getDevice(deviceId)->configuration.addProperty(key, value); +} + +void FakeEventHub::addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) { + getDevice(deviceId)->configuration.addAll(configuration); +} + +void FakeEventHub::addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, + int flat, int fuzz, int resolution) { + Device* device = getDevice(deviceId); + + RawAbsoluteAxisInfo info; + info.valid = true; + info.minValue = minValue; + info.maxValue = maxValue; + info.flat = flat; + info.fuzz = fuzz; + info.resolution = resolution; + device->absoluteAxes.add(axis, info); +} + +void FakeEventHub::addRelativeAxis(int32_t deviceId, int32_t axis) { + getDevice(deviceId)->relativeAxes.add(axis, true); +} + +void FakeEventHub::setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) { + getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state); +} + +void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) { + getDevice(deviceId)->countryCode = countryCode; +} + +void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { + getDevice(deviceId)->scanCodeStates.replaceValueFor(scanCode, state); +} + +void FakeEventHub::setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) { + getDevice(deviceId)->switchStates.replaceValueFor(switchCode, state); +} + +void FakeEventHub::setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) { + getDevice(deviceId)->absoluteAxisValue.replaceValueFor(axis, value); +} + +void FakeEventHub::addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, + uint32_t flags) { + Device* device = getDevice(deviceId); + KeyInfo info; + info.keyCode = keyCode; + info.flags = flags; + if (scanCode) { + device->keysByScanCode.add(scanCode, info); + } + if (usageCode) { + device->keysByUsageCode.add(usageCode, info); + } +} + +void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) { + getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); +} + +void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { + Device* device = getDevice(deviceId); + device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode); +} + +void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) { + getDevice(deviceId)->leds.add(led, initialState); +} + +void FakeEventHub::addSensorAxis(int32_t deviceId, int32_t absCode, + InputDeviceSensorType sensorType, int32_t sensorDataIndex) { + SensorInfo info; + info.sensorType = sensorType; + info.sensorDataIndex = sensorDataIndex; + getDevice(deviceId)->sensorsByAbsCode.emplace(absCode, info); +} + +void FakeEventHub::setMscEvent(int32_t deviceId, int32_t mscEvent) { + typename BitArray<MSC_MAX>::Buffer buffer; + buffer[mscEvent / 32] = 1 << mscEvent % 32; + getDevice(deviceId)->mscBitmask.loadFromBuffer(buffer); +} + +void FakeEventHub::addRawLightInfo(int32_t rawId, RawLightInfo&& info) { + mRawLightInfos.emplace(rawId, std::move(info)); +} + +void FakeEventHub::fakeLightBrightness(int32_t rawId, int32_t brightness) { + mLightBrightness.emplace(rawId, brightness); +} + +void FakeEventHub::fakeLightIntensities(int32_t rawId, + const std::unordered_map<LightColor, int32_t> intensities) { + mLightIntensities.emplace(rawId, std::move(intensities)); +} + +bool FakeEventHub::getLedState(int32_t deviceId, int32_t led) { + return getDevice(deviceId)->leds.valueFor(led); +} + +std::vector<std::string>& FakeEventHub::getExcludedDevices() { + return mExcludedDevices; +} + +void FakeEventHub::addVirtualKeyDefinition(int32_t deviceId, + const VirtualKeyDefinition& definition) { + getDevice(deviceId)->virtualKeys.push_back(definition); +} + +void FakeEventHub::enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, + int32_t code, int32_t value) { + std::scoped_lock<std::mutex> lock(mLock); + RawEvent event; + event.when = when; + event.readTime = readTime; + event.deviceId = deviceId; + event.type = type; + event.code = code; + event.value = value; + mEvents.push_back(event); + + if (type == EV_ABS) { + setAbsoluteAxisValue(deviceId, code, value); + } +} + +void FakeEventHub::setVideoFrames( + std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) { + mVideoFrames = std::move(videoFrames); +} + +void FakeEventHub::assertQueueIsEmpty() { + std::unique_lock<std::mutex> lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + const bool queueIsEmpty = + mEventsCondition.wait_for(lock, WAIT_TIMEOUT, + [this]() REQUIRES(mLock) { return mEvents.size() == 0; }); + if (!queueIsEmpty) { + FAIL() << "Timed out waiting for EventHub queue to be emptied."; + } +} + +FakeEventHub::Device* FakeEventHub::getDevice(int32_t deviceId) const { + ssize_t index = mDevices.indexOfKey(deviceId); + return index >= 0 ? mDevices.valueAt(index) : nullptr; +} + +ftl::Flags<InputDeviceClass> FakeEventHub::getDeviceClasses(int32_t deviceId) const { + Device* device = getDevice(deviceId); + return device ? device->classes : ftl::Flags<InputDeviceClass>(0); +} + +InputDeviceIdentifier FakeEventHub::getDeviceIdentifier(int32_t deviceId) const { + Device* device = getDevice(deviceId); + return device ? device->identifier : InputDeviceIdentifier(); +} + +int32_t FakeEventHub::getDeviceControllerNumber(int32_t) const { + return 0; +} + +void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { + Device* device = getDevice(deviceId); + if (device) { + *outConfiguration = device->configuration; + } +} + +status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->absoluteAxes.indexOfKey(axis); + if (index >= 0) { + *outAxisInfo = device->absoluteAxes.valueAt(index); + return OK; + } + } + outAxisInfo->clear(); + return -1; +} + +bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const { + Device* device = getDevice(deviceId); + if (device) { + return device->relativeAxes.indexOfKey(axis) >= 0; + } + return false; +} + +bool FakeEventHub::hasInputProperty(int32_t, int) const { + return false; +} + +bool FakeEventHub::hasMscEvent(int32_t deviceId, int mscEvent) const { + Device* device = getDevice(deviceId); + if (device) { + return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false; + } + return false; +} + +status_t FakeEventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const { + Device* device = getDevice(deviceId); + if (device) { + const KeyInfo* key = getKey(device, scanCode, usageCode); + if (key) { + if (outKeycode) { + auto it = device->keyRemapping.find(key->keyCode); + *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode; + } + if (outFlags) { + *outFlags = key->flags; + } + if (outMetaState) { + *outMetaState = metaState; + } + return OK; + } + } + return NAME_NOT_FOUND; +} + +const FakeEventHub::KeyInfo* FakeEventHub::getKey(Device* device, int32_t scanCode, + int32_t usageCode) const { + if (usageCode) { + ssize_t index = device->keysByUsageCode.indexOfKey(usageCode); + if (index >= 0) { + return &device->keysByUsageCode.valueAt(index); + } + } + if (scanCode) { + ssize_t index = device->keysByScanCode.indexOfKey(scanCode); + if (index >= 0) { + return &device->keysByScanCode.valueAt(index); + } + } + return nullptr; +} + +status_t FakeEventHub::mapAxis(int32_t, int32_t, AxisInfo*) const { + return NAME_NOT_FOUND; +} + +base::Result<std::pair<InputDeviceSensorType, int32_t>> FakeEventHub::mapSensor( + int32_t deviceId, int32_t absCode) const { + Device* device = getDevice(deviceId); + if (!device) { + return Errorf("Sensor device not found."); + } + auto it = device->sensorsByAbsCode.find(absCode); + if (it == device->sensorsByAbsCode.end()) { + return Errorf("Sensor map not found."); + } + const SensorInfo& info = it->second; + return std::make_pair(info.sensorType, info.sensorDataIndex); +} + +void FakeEventHub::setExcludedDevices(const std::vector<std::string>& devices) { + mExcludedDevices = devices; +} + +std::vector<RawEvent> FakeEventHub::getEvents(int) { + std::scoped_lock lock(mLock); + + std::vector<RawEvent> buffer; + std::swap(buffer, mEvents); + + mEventsCondition.notify_all(); + return buffer; +} + +std::vector<TouchVideoFrame> FakeEventHub::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 {}; +} + +int32_t FakeEventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->scanCodeStates.indexOfKey(scanCode); + if (index >= 0) { + return device->scanCodeStates.valueAt(index); + } + } + return AKEY_STATE_UNKNOWN; +} + +InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const { + Device* device = getDevice(deviceId); + return device ? device->countryCode : InputDeviceCountryCode::INVALID; +} + +int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->keyCodeStates.indexOfKey(keyCode); + if (index >= 0) { + return device->keyCodeStates.valueAt(index); + } + } + return AKEY_STATE_UNKNOWN; +} + +int32_t FakeEventHub::getSwitchState(int32_t deviceId, int32_t sw) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->switchStates.indexOfKey(sw); + if (index >= 0) { + return device->switchStates.valueAt(index); + } + } + return AKEY_STATE_UNKNOWN; +} + +status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->absoluteAxisValue.indexOfKey(axis); + if (index >= 0) { + *outValue = device->absoluteAxisValue.valueAt(index); + return OK; + } + } + *outValue = 0; + return -1; +} + +int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { + Device* device = getDevice(deviceId); + if (!device) { + return AKEYCODE_UNKNOWN; + } + auto it = device->keyCodeMapping.find(locationKeyCode); + return it != device->keyCodeMapping.end() ? it->second : locationKeyCode; +} + +// Return true if the device has non-empty key layout. +bool FakeEventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) const { + Device* device = getDevice(deviceId); + if (!device) return false; + + bool result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0; + for (size_t i = 0; i < keyCodes.size(); i++) { + for (size_t j = 0; j < device->keysByScanCode.size(); j++) { + if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) { + outFlags[i] = 1; + } + } + for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { + if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) { + outFlags[i] = 1; + } + } + } + return result; +} + +bool FakeEventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->keysByScanCode.indexOfKey(scanCode); + return index >= 0; + } + return false; +} + +bool FakeEventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const { + Device* device = getDevice(deviceId); + if (!device) { + return false; + } + for (size_t i = 0; i < device->keysByScanCode.size(); i++) { + if (keyCode == device->keysByScanCode.valueAt(i).keyCode) { + return true; + } + } + for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { + if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) { + return true; + } + } + return false; +} + +bool FakeEventHub::hasLed(int32_t deviceId, int32_t led) const { + Device* device = getDevice(deviceId); + return device && device->leds.indexOfKey(led) >= 0; +} + +void FakeEventHub::setLedState(int32_t deviceId, int32_t led, bool on) { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->leds.indexOfKey(led); + if (index >= 0) { + device->leds.replaceValueAt(led, on); + } else { + ADD_FAILURE() << "Attempted to set the state of an LED that the EventHub declared " + "was not present. led=" + << led; + } + } +} + +void FakeEventHub::getVirtualKeyDefinitions( + int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const { + outVirtualKeys.clear(); + + Device* device = getDevice(deviceId); + if (device) { + outVirtualKeys = device->virtualKeys; + } +} + +const std::shared_ptr<KeyCharacterMap> FakeEventHub::getKeyCharacterMap(int32_t) const { + return nullptr; +} + +bool FakeEventHub::setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) { + return false; +} + +std::vector<int32_t> FakeEventHub::getVibratorIds(int32_t deviceId) const { + return mVibrators; +} + +std::optional<int32_t> FakeEventHub::getBatteryCapacity(int32_t, int32_t) const { + return BATTERY_CAPACITY; +} + +std::optional<int32_t> FakeEventHub::getBatteryStatus(int32_t, int32_t) const { + return BATTERY_STATUS; +} + +std::vector<int32_t> FakeEventHub::getRawBatteryIds(int32_t deviceId) const { + return {DEFAULT_BATTERY}; +} + +std::optional<RawBatteryInfo> FakeEventHub::getRawBatteryInfo(int32_t deviceId, + int32_t batteryId) const { + if (batteryId != DEFAULT_BATTERY) return {}; + static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY, + .name = "default battery", + .flags = InputBatteryClass::CAPACITY, + .path = BATTERY_DEVPATH}; + return BATTERY_INFO; +} + +std::vector<int32_t> FakeEventHub::getRawLightIds(int32_t deviceId) const { + std::vector<int32_t> ids; + for (const auto& [rawId, info] : mRawLightInfos) { + ids.push_back(rawId); + } + return ids; +} + +std::optional<RawLightInfo> FakeEventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const { + auto it = mRawLightInfos.find(lightId); + if (it == mRawLightInfos.end()) { + return std::nullopt; + } + return it->second; +} + +void FakeEventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) { + mLightBrightness.emplace(lightId, brightness); +} + +void FakeEventHub::setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) { + mLightIntensities.emplace(lightId, intensities); +}; + +std::optional<int32_t> FakeEventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const { + auto lightIt = mLightBrightness.find(lightId); + if (lightIt == mLightBrightness.end()) { + return std::nullopt; + } + return lightIt->second; +} + +std::optional<std::unordered_map<LightColor, int32_t>> FakeEventHub::getLightIntensities( + int32_t deviceId, int32_t lightId) const { + auto lightIt = mLightIntensities.find(lightId); + if (lightIt == mLightIntensities.end()) { + return std::nullopt; + } + return lightIt->second; +}; + +} // namespace android diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h new file mode 100644 index 0000000000..fb3c8596c7 --- /dev/null +++ b/services/inputflinger/tests/FakeEventHub.h @@ -0,0 +1,226 @@ +/* + * Copyright 2022 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 <condition_variable> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <vector> + +#include <EventHub.h> +#include <InputDevice.h> +#include <ftl/flags.h> +#include <input/PropertyMap.h> +#include <input/VirtualKeyMap.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> + +#include "android/hardware/input/InputDeviceCountryCode.h" + +using android::hardware::input::InputDeviceCountryCode; + +namespace android { + +class FakeEventHub : public EventHubInterface { + struct KeyInfo { + int32_t keyCode; + uint32_t flags; + }; + + struct SensorInfo { + InputDeviceSensorType sensorType; + int32_t sensorDataIndex; + }; + + struct Device { + InputDeviceIdentifier identifier; + ftl::Flags<InputDeviceClass> classes; + PropertyMap configuration; + KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes; + KeyedVector<int, bool> relativeAxes; + KeyedVector<int32_t, int32_t> keyCodeStates; + KeyedVector<int32_t, int32_t> scanCodeStates; + KeyedVector<int32_t, int32_t> switchStates; + KeyedVector<int32_t, int32_t> absoluteAxisValue; + KeyedVector<int32_t, KeyInfo> keysByScanCode; + KeyedVector<int32_t, KeyInfo> keysByUsageCode; + std::unordered_map<int32_t, int32_t> keyRemapping; + KeyedVector<int32_t, bool> leds; + // fake mapping which would normally come from keyCharacterMap + std::unordered_map<int32_t, int32_t> keyCodeMapping; + std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode; + BitArray<MSC_MAX> mscBitmask; + std::vector<VirtualKeyDefinition> virtualKeys; + bool enabled; + InputDeviceCountryCode countryCode; + + status_t enable() { + enabled = true; + return OK; + } + + status_t disable() { + enabled = false; + return OK; + } + + explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {} + }; + + std::mutex mLock; + std::condition_variable mEventsCondition; + + KeyedVector<int32_t, Device*> mDevices; + std::vector<std::string> mExcludedDevices; + std::vector<RawEvent> mEvents GUARDED_BY(mLock); + std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; + std::vector<int32_t> mVibrators = {0, 1}; + std::unordered_map<int32_t, RawLightInfo> mRawLightInfos; + // Simulates a device light brightness, from light id to light brightness. + std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness; + // Simulates a device light intensities, from light id to light intensities map. + std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>> + mLightIntensities; + +public: + static constexpr int32_t DEFAULT_BATTERY = 1; + static constexpr int32_t BATTERY_STATUS = 4; + static constexpr int32_t BATTERY_CAPACITY = 66; + static const std::string BATTERY_DEVPATH; + + virtual ~FakeEventHub(); + FakeEventHub() {} + + void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes, + int bus = 0); + void removeDevice(int32_t deviceId); + + bool isDeviceEnabled(int32_t deviceId) const override; + status_t enableDevice(int32_t deviceId) override; + status_t disableDevice(int32_t deviceId) override; + + void finishDeviceScan(); + + void addConfigurationProperty(int32_t deviceId, const char* key, const char* value); + void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration); + + void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat, + int fuzz, int resolution = 0); + void addRelativeAxis(int32_t deviceId, int32_t axis); + void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value); + + void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode); + + void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state); + void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state); + void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state); + + void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, + uint32_t flags); + void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode); + void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const; + void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition); + + void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, + int32_t sensorDataIndex); + + void setMscEvent(int32_t deviceId, int32_t mscEvent); + + void addLed(int32_t deviceId, int32_t led, bool initialState); + void addRawLightInfo(int32_t rawId, RawLightInfo&& info); + void fakeLightBrightness(int32_t rawId, int32_t brightness); + void fakeLightIntensities(int32_t rawId, + const std::unordered_map<LightColor, int32_t> intensities); + bool getLedState(int32_t deviceId, int32_t led); + + std::vector<std::string>& getExcludedDevices(); + + void setVideoFrames( + std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames); + + void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code, + int32_t value); + void assertQueueIsEmpty(); + +private: + Device* getDevice(int32_t deviceId) const; + + ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override; + InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override; + int32_t getDeviceControllerNumber(int32_t) const override; + void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override; + status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const override; + bool hasRelativeAxis(int32_t deviceId, int axis) const override; + bool hasInputProperty(int32_t, int) const override; + bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override; + const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const; + + status_t mapAxis(int32_t, int32_t, AxisInfo*) const override; + base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor( + int32_t deviceId, int32_t absCode) const override; + void setExcludedDevices(const std::vector<std::string>& devices) override; + std::vector<RawEvent> getEvents(int) override; + std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override; + int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override; + InputDeviceCountryCode getCountryCode(int32_t deviceId) const override; + int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override; + int32_t getSwitchState(int32_t deviceId, int32_t sw) const override; + status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override; + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; + + // Return true if the device has non-empty key layout. + bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) const override; + bool hasScanCode(int32_t deviceId, int32_t scanCode) const override; + bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override; + bool hasLed(int32_t deviceId, int32_t led) const override; + void setLedState(int32_t deviceId, int32_t led, bool on) override; + void getVirtualKeyDefinitions(int32_t deviceId, + std::vector<VirtualKeyDefinition>& outVirtualKeys) const override; + const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override; + bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override; + + void vibrate(int32_t, const VibrationElement&) override {} + void cancelVibrate(int32_t) override {} + std::vector<int32_t> getVibratorIds(int32_t deviceId) const override; + + std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override; + std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override; + std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override; + std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, + int32_t batteryId) const override; + + std::vector<int32_t> getRawLightIds(int32_t deviceId) const override; + std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override; + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override; + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) override; + std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override; + std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t deviceId, int32_t lightId) const override; + + void dump(std::string&) const override {} + void monitor() const override {} + void requestReopenDevices() override {} + void wake() override {} +}; + +} // namespace android diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp new file mode 100644 index 0000000000..3af4298434 --- /dev/null +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2022 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 "FakeInputReaderPolicy.h" + +#include <android-base/thread_annotations.h> +#include <gtest/gtest.h> + +#include "TestConstants.h" +#include "ui/Rotation.h" + +namespace android { + +void FakeInputReaderPolicy::assertInputDevicesChanged() { + waitForInputDevices([](bool devicesChanged) { + if (!devicesChanged) { + FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; + } + }); +} + +void FakeInputReaderPolicy::assertInputDevicesNotChanged() { + waitForInputDevices([](bool devicesChanged) { + if (devicesChanged) { + FAIL() << "Expected notifyInputDevicesChanged() to not be called."; + } + }); +} + +void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mStylusGestureNotified); + ASSERT_EQ(deviceId, *mStylusGestureNotified); + mStylusGestureNotified.reset(); +} + +void FakeInputReaderPolicy::assertStylusGestureNotNotified() { + std::scoped_lock lock(mLock); + ASSERT_FALSE(mStylusGestureNotified); +} + +void FakeInputReaderPolicy::clearViewports() { + mViewports.clear(); + mConfig.setDisplayViewports(mViewports); +} + +std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByUniqueId( + const std::string& uniqueId) const { + return mConfig.getDisplayViewportByUniqueId(uniqueId); +} +std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByType( + ViewportType type) const { + return mConfig.getDisplayViewportByType(type); +} + +std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByPort( + uint8_t displayPort) const { + return mConfig.getDisplayViewportByPort(displayPort); +} + +void FakeInputReaderPolicy::addDisplayViewport(DisplayViewport viewport) { + mViewports.push_back(std::move(viewport)); + mConfig.setDisplayViewports(mViewports); +} + +void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height, + ui::Rotation orientation, bool isActive, + const std::string& uniqueId, + std::optional<uint8_t> physicalPort, + ViewportType type) { + const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270; + DisplayViewport v; + v.displayId = displayId; + v.orientation = orientation; + v.logicalLeft = 0; + v.logicalTop = 0; + v.logicalRight = isRotated ? height : width; + v.logicalBottom = isRotated ? width : height; + v.physicalLeft = 0; + v.physicalTop = 0; + v.physicalRight = isRotated ? height : width; + v.physicalBottom = isRotated ? width : height; + v.deviceWidth = isRotated ? height : width; + v.deviceHeight = isRotated ? width : height; + v.isActive = isActive; + v.uniqueId = uniqueId; + v.physicalPort = physicalPort; + v.type = type; + + addDisplayViewport(v); +} + +bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) { + size_t count = mViewports.size(); + for (size_t i = 0; i < count; i++) { + const DisplayViewport& currentViewport = mViewports[i]; + if (currentViewport.displayId == viewport.displayId) { + mViewports[i] = viewport; + mConfig.setDisplayViewports(mViewports); + return true; + } + } + // no viewport found. + return false; +} + +void FakeInputReaderPolicy::addExcludedDeviceName(const std::string& deviceName) { + mConfig.excludedDeviceNames.push_back(deviceName); +} + +void FakeInputReaderPolicy::addInputPortAssociation(const std::string& inputPort, + uint8_t displayPort) { + mConfig.portAssociations.insert({inputPort, displayPort}); +} + +void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId, + const std::string& displayUniqueId) { + mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId}); +} + +void FakeInputReaderPolicy::addDisabledDevice(int32_t deviceId) { + mConfig.disabledDevices.insert(deviceId); +} + +void FakeInputReaderPolicy::removeDisabledDevice(int32_t deviceId) { + mConfig.disabledDevices.erase(deviceId); +} + +void FakeInputReaderPolicy::setPointerController( + std::shared_ptr<FakePointerController> controller) { + mPointerController = std::move(controller); +} + +const InputReaderConfiguration* FakeInputReaderPolicy::getReaderConfiguration() const { + return &mConfig; +} + +const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const { + return mInputDevices; +} + +TouchAffineTransformation FakeInputReaderPolicy::getTouchAffineTransformation( + const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) { + return transform; +} + +void FakeInputReaderPolicy::setTouchAffineTransformation(const TouchAffineTransformation t) { + transform = t; +} + +PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) { + mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++}; + return mConfig.pointerCaptureRequest; +} + +void FakeInputReaderPolicy::setShowTouches(bool enabled) { + mConfig.showTouches = enabled; +} + +void FakeInputReaderPolicy::setDefaultPointerDisplayId(int32_t pointerDisplayId) { + mConfig.defaultPointerDisplayId = pointerDisplayId; +} + +void FakeInputReaderPolicy::setPointerGestureEnabled(bool enabled) { + mConfig.pointerGesturesEnabled = enabled; +} + +float FakeInputReaderPolicy::getPointerGestureMovementSpeedRatio() { + return mConfig.pointerGestureMovementSpeedRatio; +} + +float FakeInputReaderPolicy::getPointerGestureZoomSpeedRatio() { + return mConfig.pointerGestureZoomSpeedRatio; +} + +void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) { + mConfig.pointerVelocityControlParameters = params; + mConfig.wheelVelocityControlParameters = params; +} + +void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) { + *outConfig = mConfig; +} + +std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointerController( + int32_t /*deviceId*/) { + return mPointerController; +} + +void FakeInputReaderPolicy::notifyInputDevicesChanged( + const std::vector<InputDeviceInfo>& inputDevices) { + std::scoped_lock<std::mutex> lock(mLock); + mInputDevices = inputDevices; + mInputDevicesChanged = true; + mDevicesChangedCondition.notify_all(); +} + +std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( + const InputDeviceIdentifier&) { + return nullptr; +} + +std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) { + return ""; +} + +void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) { + std::unique_lock<std::mutex> lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool devicesChanged = + mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mInputDevicesChanged; + }); + ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged)); + mInputDevicesChanged = false; +} + +void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) { + std::scoped_lock<std::mutex> lock(mLock); + mStylusGestureNotified = deviceId; +} + +} // namespace android diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h new file mode 100644 index 0000000000..c16cda404e --- /dev/null +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -0,0 +1,101 @@ +/* + * Copyright 2022 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 <condition_variable> +#include <memory> +#include <mutex> +#include <optional> +#include <string> +#include <vector> + +#include <InputDevice.h> +#include <InputReaderBase.h> + +#include "FakePointerController.h" +#include "input/DisplayViewport.h" +#include "input/InputDevice.h" + +namespace android { + +class FakeInputReaderPolicy : public InputReaderPolicyInterface { +protected: + virtual ~FakeInputReaderPolicy() {} + +public: + FakeInputReaderPolicy() {} + + void assertInputDevicesChanged(); + void assertInputDevicesNotChanged(); + void assertStylusGestureNotified(int32_t deviceId); + void assertStylusGestureNotNotified(); + + virtual void clearViewports(); + std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const; + std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const; + std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const; + void addDisplayViewport(DisplayViewport viewport); + void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, + ui::Rotation orientation, bool isActive, const std::string& uniqueId, + std::optional<uint8_t> physicalPort, ViewportType type); + bool updateViewport(const DisplayViewport& viewport); + void addExcludedDeviceName(const std::string& deviceName); + void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort); + void addInputUniqueIdAssociation(const std::string& inputUniqueId, + const std::string& displayUniqueId); + void addDisabledDevice(int32_t deviceId); + void removeDisabledDevice(int32_t deviceId); + void setPointerController(std::shared_ptr<FakePointerController> controller); + const InputReaderConfiguration* getReaderConfiguration() const; + const std::vector<InputDeviceInfo>& getInputDevices() const; + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, + ui::Rotation surfaceRotation); + void setTouchAffineTransformation(const TouchAffineTransformation t); + PointerCaptureRequest setPointerCapture(bool enabled); + void setShowTouches(bool enabled); + void setDefaultPointerDisplayId(int32_t pointerDisplayId); + void setPointerGestureEnabled(bool enabled); + float getPointerGestureMovementSpeedRatio(); + float getPointerGestureZoomSpeedRatio(); + void setVelocityControlParams(const VelocityControlParameters& params); + +private: + void getReaderConfiguration(InputReaderConfiguration* outConfig) override; + std::shared_ptr<PointerControllerInterface> obtainPointerController( + int32_t /*deviceId*/) override; + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier&) override; + std::string getDeviceAlias(const InputDeviceIdentifier&) override; + void waitForInputDevices(std::function<void(bool)> processDevicesChanged); + void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override; + + std::mutex mLock; + std::condition_variable mDevicesChangedCondition; + + InputReaderConfiguration mConfig; + std::shared_ptr<FakePointerController> mPointerController; + std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock); + bool mInputDevicesChanged GUARDED_BY(mLock){false}; + std::vector<DisplayViewport> mViewports; + TouchAffineTransformation transform; + std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){}; + + uint32_t mNextPointerCaptureSequenceNumber{0}; +}; + +} // namespace android diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp new file mode 100644 index 0000000000..635366bff2 --- /dev/null +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2022 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 "FakePointerController.h" + +namespace android { + +void FakePointerController::setBounds(float minX, float minY, float maxX, float maxY) { + mHaveBounds = true; + mMinX = minX; + mMinY = minY; + mMaxX = maxX; + mMaxY = maxY; +} + +const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() { + return mSpotsByDisplay; +} + +void FakePointerController::setPosition(float x, float y) { + mX = x; + mY = y; +} + +void FakePointerController::setButtonState(int32_t buttonState) { + mButtonState = buttonState; +} + +int32_t FakePointerController::getButtonState() const { + return mButtonState; +} + +void FakePointerController::getPosition(float* outX, float* outY) const { + *outX = mX; + *outY = mY; +} + +int32_t FakePointerController::getDisplayId() const { + return mDisplayId; +} + +void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) { + mDisplayId = viewport.displayId; +} + +bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + *outMinX = mMinX; + *outMinY = mMinY; + *outMaxX = mMaxX; + *outMaxY = mMaxY; + return mHaveBounds; +} + +void FakePointerController::move(float deltaX, float deltaY) { + mX += deltaX; + if (mX < mMinX) mX = mMinX; + if (mX > mMaxX) mX = mMaxX; + mY += deltaY; + if (mY < mMinY) mY = mMinY; + if (mY > mMaxY) mY = mMaxY; +} + +void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, + int32_t displayId) { + std::vector<int32_t> newSpots; + // Add spots for fingers that are down. + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + newSpots.push_back(id); + } + + mSpotsByDisplay[displayId] = newSpots; +} + +void FakePointerController::clearSpots() { + mSpotsByDisplay.clear(); +} + +} // namespace android diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h new file mode 100644 index 0000000000..f00870f13c --- /dev/null +++ b/services/inputflinger/tests/FakePointerController.h @@ -0,0 +1,60 @@ +/* + * Copyright 2022 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 <PointerControllerInterface.h> +#include <gui/constants.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <utils/BitSet.h> + +namespace android { + +class FakePointerController : public PointerControllerInterface { +public: + virtual ~FakePointerController() {} + + void setBounds(float minX, float minY, float maxX, float maxY); + const std::map<int32_t, std::vector<int32_t>>& getSpots(); + + void setPosition(float x, float y) override; + void setButtonState(int32_t buttonState) override; + int32_t getButtonState() const override; + void getPosition(float* outX, float* outY) const override; + int32_t getDisplayId() const override; + void setDisplayViewport(const DisplayViewport& viewport) override; + +private: + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override; + void move(float deltaX, float deltaY) override; + void fade(Transition) override {} + void unfade(Transition) override {} + void setPresentation(Presentation) override {} + void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, + int32_t displayId) override; + void clearSpots() override; + + bool mHaveBounds{false}; + float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0}; + float mX{0}, mY{0}; + int32_t mButtonState{0}; + int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + + std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; +}; + +} // namespace android diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp index 91be4a30bf..5d5cf9c108 100644 --- a/services/inputflinger/tests/FocusResolver_test.cpp +++ b/services/inputflinger/tests/FocusResolver_test.cpp @@ -50,16 +50,16 @@ public: }; TEST(FocusResolverTest, SetFocusedWindow) { - sp<IBinder> focusableWindowToken = new BBinder(); - sp<IBinder> invisibleWindowToken = new BBinder(); - sp<IBinder> unfocusableWindowToken = new BBinder(); + sp<IBinder> focusableWindowToken = sp<BBinder>::make(); + sp<IBinder> invisibleWindowToken = sp<BBinder>::make(); + sp<IBinder> unfocusableWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */, - true /* visible */)); - windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */, - false /* visible */)); - windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken, - false /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Focusable", focusableWindowToken, + true /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Invisible", invisibleWindowToken, + true /* focusable */, false /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("unfocusable", unfocusableWindowToken, + false /* focusable */, true /* visible */)); // focusable window can get focused FocusRequest request; @@ -85,10 +85,10 @@ TEST(FocusResolverTest, SetFocusedWindow) { } TEST(FocusResolverTest, RemoveFocusFromFocusedWindow) { - sp<IBinder> focusableWindowToken = new BBinder(); + sp<IBinder> focusableWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */, - true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Focusable", focusableWindowToken, + true /* focusable */, true /* visible */)); FocusRequest request; request.displayId = 42; @@ -109,24 +109,24 @@ TEST(FocusResolverTest, RemoveFocusFromFocusedWindow) { } TEST(FocusResolverTest, SetFocusedMirroredWindow) { - sp<IBinder> focusableWindowToken = new BBinder(); - sp<IBinder> invisibleWindowToken = new BBinder(); - sp<IBinder> unfocusableWindowToken = new BBinder(); + sp<IBinder> focusableWindowToken = sp<BBinder>::make(); + sp<IBinder> invisibleWindowToken = sp<BBinder>::make(); + sp<IBinder> unfocusableWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */, - true /* visible */)); - windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */, - true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror1", focusableWindowToken, + true /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror1", focusableWindowToken, + true /* focusable */, true /* visible */)); - windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken, - true /* focusable */, true /* visible */)); - windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken, - true /* focusable */, false /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror2Visible", invisibleWindowToken, + true /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror2Invisible", invisibleWindowToken, + true /* focusable */, false /* visible */)); - windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken, - true /* focusable */, true /* visible */)); - windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken, - false /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror3Focusable", unfocusableWindowToken, + true /* focusable */, true /* visible */)); + windows.push_back(sp<FakeWindowHandle>::make("Mirror3Unfocusable", unfocusableWindowToken, + false /* focusable */, true /* visible */)); // mirrored window can get focused FocusRequest request; @@ -149,10 +149,11 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) { } TEST(FocusResolverTest, SetInputWindows) { - sp<IBinder> focusableWindowToken = new BBinder(); + sp<IBinder> focusableWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken, - true /* focusable */, true /* visible */); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make("Focusable", focusableWindowToken, true /* focusable */, + true /* visible */); windows.push_back(window); // focusable window can get focused @@ -171,12 +172,12 @@ TEST(FocusResolverTest, SetInputWindows) { } TEST(FocusResolverTest, FocusRequestsCanBePending) { - sp<IBinder> invisibleWindowToken = new BBinder(); + sp<IBinder> invisibleWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> invisibleWindow = - new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */, - false /* visible */); + sp<FakeWindowHandle>::make("Invisible", invisibleWindowToken, true /* focusable */, + false /* visible */); windows.push_back(invisibleWindow); // invisible window cannot get focused @@ -195,11 +196,12 @@ TEST(FocusResolverTest, FocusRequestsCanBePending) { } TEST(FocusResolverTest, FocusRequestsArePersistent) { - sp<IBinder> windowToken = new BBinder(); + sp<IBinder> windowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken, - false /* focusable */, true /* visible */); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make("Test Window", windowToken, false /* focusable */, + true /* visible */); windows.push_back(window); // non-focusable window cannot get focused @@ -236,17 +238,17 @@ TEST(FocusResolverTest, FocusRequestsArePersistent) { } TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { - sp<IBinder> hostWindowToken = new BBinder(); + sp<IBinder> hostWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; sp<FakeWindowHandle> hostWindow = - new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */, - true /* visible */); + sp<FakeWindowHandle>::make("Host Window", hostWindowToken, true /* focusable */, + true /* visible */); windows.push_back(hostWindow); - sp<IBinder> embeddedWindowToken = new BBinder(); + sp<IBinder> embeddedWindowToken = sp<BBinder>::make(); sp<FakeWindowHandle> embeddedWindow = - new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */, - true /* visible */); + sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, true /* focusable */, + true /* visible */); windows.push_back(embeddedWindow); FocusRequest request; @@ -287,11 +289,12 @@ TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { ASSERT_FALSE(changes); } TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) { - sp<IBinder> windowToken = new BBinder(); + sp<IBinder> windowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; - sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken, - true /* focusable */, true /* visible */); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make("Test Window", windowToken, true /* focusable */, + true /* visible */); windows.push_back(window); FocusRequest request; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index fce0f999b5..41c174a1f7 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -22,6 +22,7 @@ #include <android-base/thread_annotations.h> #include <binder/Binder.h> #include <fcntl.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/Input.h> #include <linux/input.h> @@ -43,6 +44,7 @@ using android::os::InputEventInjectionSync; namespace android::inputdispatcher { using namespace ftl::flag_operators; +using testing::AllOf; // An arbitrary time value. static constexpr nsecs_t ARBITRARY_TIME = 1234; @@ -58,13 +60,19 @@ static constexpr int32_t POINTER_1_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_2_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +static constexpr int32_t POINTER_3_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -// The default pid and uid for windows created by the test. +// The default pid and uid for windows created on the primary display by the test. static constexpr int32_t WINDOW_PID = 999; static constexpr int32_t WINDOW_UID = 1001; +// The default pid and uid for the windows created on the secondary display by the test. +static constexpr int32_t SECONDARY_WINDOW_PID = 1010; +static constexpr int32_t SECONDARY_WINDOW_UID = 1012; + // The default policy flags to use for event injection by tests. static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER; @@ -96,6 +104,39 @@ static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) { << MotionEvent::actionToString(receivedAction); } +MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") { + bool matches = action == arg.getAction(); + if (!matches) { + *result_listener << "expected action " << MotionEvent::actionToString(action) + << ", but got " << MotionEvent::actionToString(arg.getAction()); + } + if (action == AMOTION_EVENT_ACTION_DOWN) { + if (!matches) { + *result_listener << "; "; + } + *result_listener << "downTime should match eventTime for ACTION_DOWN events"; + matches &= arg.getDownTime() == arg.getEventTime(); + } + if (action == AMOTION_EVENT_ACTION_CANCEL) { + if (!matches) { + *result_listener << "; "; + } + *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set"; + matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; +} + +MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") { + return arg.getDownTime() == downTime; +} + +MATCHER_P(WithSource, source, "InputEvent with specified source") { + *result_listener << "expected source " << inputEventSourceToString(source) << ", but got " + << inputEventSourceToString(arg.getSource()); + return arg.getSource() == source; +} + // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { @@ -412,7 +453,6 @@ private: void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} - void notifyUntrustedTouch(const std::string& obscuringPackage) override {} void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, const std::vector<float>& values) override {} @@ -509,7 +549,7 @@ protected: std::unique_ptr<InputDispatcher> mDispatcher; void SetUp() override { - mFakePolicy = new FakeInputDispatcherPolicy(); + mFakePolicy = sp<FakeInputDispatcherPolicy>::make(); mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); // Start InputDispatcher thread @@ -746,7 +786,7 @@ class FakeApplicationHandle : public InputApplicationHandle { public: FakeApplicationHandle() { mInfo.name = "Fake Application"; - mInfo.token = new BBinder(); + mInfo.token = sp<BBinder>::make(); mInfo.dispatchingTimeoutMillis = std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); } @@ -1022,8 +1062,8 @@ public: const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) { sp<FakeWindowHandle> handle = - new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)", - displayId, mInfo.token); + sp<FakeWindowHandle>::make(inputApplicationHandle, dispatcher, + mInfo.name + "(Mirror)", displayId, mInfo.token); return handle; } @@ -1181,6 +1221,7 @@ public: void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { InputEvent* event = consume(); + ASSERT_NE(nullptr, event); ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event); EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked()); @@ -1200,6 +1241,12 @@ public: mInputReceiver->consumeCaptureEvent(hasCapture); } + void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { + MotionEvent* motionEvent = consumeMotion(); + ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher; + ASSERT_THAT(*motionEvent, matcher); + } + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, std::optional<int32_t> expectedDisplayId, std::optional<int32_t> expectedFlags) { @@ -1557,8 +1604,8 @@ static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs( TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Window that breaks its input channel", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, + "Window that breaks its input channel", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1569,8 +1616,8 @@ TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) { TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1583,8 +1630,8 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); // Inject a MotionEvent to an unknown display. @@ -1603,8 +1650,8 @@ TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay */ TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1622,8 +1669,8 @@ TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) { */ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1641,9 +1688,9 @@ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowTop = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> windowSecond = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1665,10 +1712,10 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> foregroundWindow = - new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); foregroundWindow->setDupTouchToWallpaper(true); sp<FakeWindowHandle> wallpaperWindow = - new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); constexpr int expectedWallpaperFlags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; @@ -1709,10 +1756,10 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> foregroundWindow = - new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); foregroundWindow->setDupTouchToWallpaper(true); sp<FakeWindowHandle> wallpaperWindow = - new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); constexpr int expectedWallpaperFlags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; @@ -1753,11 +1800,11 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); window->setDupTouchToWallpaper(true); sp<FakeWindowHandle> wallpaperWindow = - new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); constexpr int expectedWallpaperFlags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; @@ -1808,17 +1855,17 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> leftWindow = - new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); leftWindow->setFrame(Rect(0, 0, 200, 200)); leftWindow->setDupTouchToWallpaper(true); sp<FakeWindowHandle> rightWindow = - new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); rightWindow->setFrame(Rect(200, 0, 400, 200)); rightWindow->setDupTouchToWallpaper(true); sp<FakeWindowHandle> wallpaperWindow = - new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setFrame(Rect(0, 0, 400, 200)); wallpaperWindow->setIsWallpaper(true); constexpr int expectedWallpaperFlags = @@ -1953,7 +2000,7 @@ TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) { TEST_F(InputDispatcherTest, SplitWorksWhenEmptyAreaIsTouched) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Window", DISPLAY_ID); + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", DISPLAY_ID); mDispatcher->setInputWindows({{DISPLAY_ID, {window}}}); NotifyMotionArgs args; @@ -1977,11 +2024,11 @@ TEST_F(InputDispatcherTest, SplitWorksWhenEmptyAreaIsTouched) { TEST_F(InputDispatcherTest, SplitWorksWhenNonTouchableWindowIsTouched) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window1 = - new FakeWindowHandle(application, mDispatcher, "Window1", DISPLAY_ID); + sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); window1->setTouchableRegion(Region{{0, 0, 100, 100}}); window1->setTouchable(false); sp<FakeWindowHandle> window2 = - new FakeWindowHandle(application, mDispatcher, "Window2", DISPLAY_ID); + sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID); window2->setTouchableRegion(Region{{100, 0, 200, 100}}); mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}}); @@ -2000,13 +2047,78 @@ TEST_F(InputDispatcherTest, SplitWorksWhenNonTouchableWindowIsTouched) { window2->consumeMotionDown(); } +/** + * When splitting touch events the downTime should be adjusted such that the downTime corresponds + * to the event time of the first ACTION_DOWN sent to the particular window. + */ +TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window1 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); + window1->setTouchableRegion(Region{{0, 0, 100, 100}}); + sp<FakeWindowHandle> window2 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID); + window2->setTouchableRegion(Region{{100, 0, 200, 100}}); + + mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}}); + + NotifyMotionArgs args; + // Touch down on the first window + mDispatcher->notifyMotion(&(args = generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}))); + + mDispatcher->waitForIdle(); + InputEvent* inputEvent1 = window1->consume(); + ASSERT_NE(inputEvent1, nullptr); + window2->assertNoEvents(); + MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1); + nsecs_t downTimeForWindow1 = motionEvent1.getDownTime(); + ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime()); + + // Now touch down on the window with another pointer + mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}))); + mDispatcher->waitForIdle(); + InputEvent* inputEvent2 = window2->consume(); + ASSERT_NE(inputEvent2, nullptr); + MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2); + nsecs_t downTimeForWindow2 = motionEvent2.getDownTime(); + ASSERT_NE(downTimeForWindow1, downTimeForWindow2); + ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime()); + + // Now move the pointer on the second window + mDispatcher->notifyMotion( + &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}))); + mDispatcher->waitForIdle(); + window2->consumeMotionEvent(WithDownTime(downTimeForWindow2)); + + // Now add new touch down on the second window + mDispatcher->notifyMotion( + &(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}}))); + mDispatcher->waitForIdle(); + window2->consumeMotionEvent(WithDownTime(downTimeForWindow2)); + + // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line + window1->consumeMotionMove(); + window1->assertNoEvents(); + + // Now move the pointer on the first window + mDispatcher->notifyMotion( + &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}}))); + mDispatcher->waitForIdle(); + window1->consumeMotionEvent(WithDownTime(downTimeForWindow1)); + + mDispatcher->notifyMotion(&( + args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}}))); + mDispatcher->waitForIdle(); + window1->consumeMotionEvent(WithDownTime(downTimeForWindow1)); +} + TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowLeft = - new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); windowLeft->setFrame(Rect(0, 0, 600, 800)); sp<FakeWindowHandle> windowRight = - new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); windowRight->setFrame(Rect(600, 0, 1200, 800)); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -2022,10 +2134,8 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(900) .y(400)) .build())); - windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); + windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); // Move cursor into left window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -2036,12 +2146,9 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(300) .y(400)) .build())); - windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)); + windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); + windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); // Inject a series of mouse events for a mouse click ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -2064,8 +2171,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(300) .y(400)) .build())); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2077,8 +2183,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(300) .y(400)) .build())); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2099,12 +2204,47 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(900) .y(400)) .build())); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)); + windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); + windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + + // No more events + windowLeft->assertNoEvents(); + windowRight->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, HoverWithSpyWindows) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> spyWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); + spyWindow->setFrame(Rect(0, 0, 600, 800)); + spyWindow->setTrustedOverlay(true); + spyWindow->setSpy(true); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 600, 800)); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}}); + + // Send mouse cursor to the window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(100) + .y(100)) + .build())); + + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithSource(AINPUT_SOURCE_MOUSE))); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithSource(AINPUT_SOURCE_MOUSE))); + + window->assertNoEvents(); + spyWindow->assertNoEvents(); } // This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected @@ -2112,7 +2252,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 1200, 800)); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -2127,8 +2267,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); // Inject a series of mouse events for a mouse click ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -2151,8 +2290,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2164,8 +2302,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2185,8 +2322,49 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)); +} + +/** + * Inject a mouse hover event followed by a tap from touchscreen. + * In the current implementation, the tap does not cause a HOVER_EXIT event. + */ +TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // Inject a hover_move from mouse. + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, {{50, 50}}); + motionArgs.xCursorPosition = 50; + motionArgs.yCursorPosition = 50; + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithSource(AINPUT_SOURCE_MOUSE)))); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE)))); + + // Tap on the window + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {{10, 10}}); + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_TOUCHSCREEN)))); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {{10, 10}}); + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithSource(AINPUT_SOURCE_TOUCHSCREEN)))); } TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) { @@ -2214,14 +2392,13 @@ TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) { .x(300) .y(600)) .build())); - windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); - windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); + windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); // Remove all windows in secondary display and check that no event happens on window in // primary display. - mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}}); windowDefaultDisplay->assertNoEvents(); // Move cursor position in window in default display and check that only hover move @@ -2237,8 +2414,9 @@ TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) { .x(400) .y(700)) .build())); - windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, - ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->consumeMotionEvent( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE))); windowDefaultDisplay->assertNoEvents(); } @@ -2246,10 +2424,10 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowLeft = - new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); windowLeft->setFrame(Rect(0, 0, 600, 800)); sp<FakeWindowHandle> windowRight = - new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); windowRight->setFrame(Rect(600, 0, 1200, 800)); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -2267,8 +2445,8 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -2292,8 +2470,8 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -2315,8 +2493,8 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { TEST_F(InputDispatcherTest, InterceptKeyByPolicy) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -2337,8 +2515,8 @@ TEST_F(InputDispatcherTest, InterceptKeyByPolicy) { TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -2365,15 +2543,17 @@ TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) { TEST_F(InputDispatcherTest, ActionOutsideSentOnlyWhenAWindowIsTouched) { // There are three windows that do not overlap. `window` wants to WATCH_OUTSIDE_TOUCH. std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "First Window", ADISPLAY_ID_DEFAULT); window->setWatchOutsideTouch(true); window->setFrame(Rect{0, 0, 100, 100}); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect{100, 100, 200, 200}); sp<FakeWindowHandle> thirdWindow = - new FakeWindowHandle(application, mDispatcher, "Third Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Third Window", + ADISPLAY_ID_DEFAULT); thirdWindow->setFrame(Rect{200, 200, 300, 300}); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, secondWindow, thirdWindow}}}); @@ -2406,8 +2586,8 @@ TEST_F(InputDispatcherTest, ActionOutsideSentOnlyWhenAWindowIsTouched) { TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); mDispatcher->onWindowInfosChanged({*window->getInfo()}, {}); @@ -2512,13 +2692,14 @@ public: // Add two windows to the display. Their frames are represented in the display space. sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform); addWindow(firstWindow); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform); addWindow(secondWindow); return {std::move(firstWindow), std::move(secondWindow)}; @@ -2619,9 +2800,11 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // Create a couple of windows sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); @@ -2670,13 +2853,13 @@ TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) { // Create a couple of windows + a spy window sp<FakeWindowHandle> spyWindow = - new FakeWindowHandle(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); spyWindow->setTrustedOverlay(true); spyWindow->setSpy(true); sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, firstWindow, secondWindow}}}); @@ -2717,10 +2900,12 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { // Create a couple of windows sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); firstWindow->setPreventSplitting(true); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setPreventSplitting(true); // Add the windows to the dispatcher @@ -2792,11 +2977,13 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); // Add the windows to the dispatcher @@ -2856,11 +3043,13 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); // Add the windows to the dispatcher @@ -2921,10 +3110,10 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindowInPrimary = - new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); sp<FakeWindowHandle> secondWindowInPrimary = - new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); sp<FakeWindowHandle> mirrorWindowInPrimary = @@ -2980,10 +3169,10 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindowInPrimary = - new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); sp<FakeWindowHandle> secondWindowInPrimary = - new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); sp<FakeWindowHandle> mirrorWindowInPrimary = @@ -3035,8 +3224,8 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3053,8 +3242,8 @@ TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3068,8 +3257,8 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) // If a window is touchable, but does not have focus, it should receive motion events, but not keys TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3091,11 +3280,13 @@ TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindow = - new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); sp<FakeWindowHandle> secondWindow = - new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); // Add the windows to the dispatcher @@ -3146,7 +3337,7 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; @@ -3236,7 +3427,7 @@ using InputDispatcherMonitorTest = InputDispatcherTest; TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDisappears) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); @@ -3276,8 +3467,8 @@ TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDis TEST_F(InputDispatcherMonitorTest, ReceivesMotionEvents) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); @@ -3293,8 +3484,8 @@ TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) { FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -3318,8 +3509,8 @@ TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) { TEST_F(InputDispatcherMonitorTest, NoWindowTransform) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->setWindowOffset(20, 40); window->setWindowTransform(0, 1, -1, 0); @@ -3347,8 +3538,8 @@ TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { TEST_F(InputDispatcherTest, TestMoveEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3378,8 +3569,8 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { */ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); const WindowInfo& windowInfo = *window->getInfo(); // Set focused application. @@ -3398,7 +3589,7 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { SCOPED_TRACE("Disable touch mode"); mDispatcher->setInTouchMode(false, windowInfo.ownerPid, windowInfo.ownerUid, - /* hasPermission */ true); + true /*hasPermission*/, ADISPLAY_ID_DEFAULT); window->consumeTouchModeEvent(false); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3412,7 +3603,7 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { SCOPED_TRACE("Enable touch mode again"); mDispatcher->setInTouchMode(true, windowInfo.ownerPid, windowInfo.ownerUid, - /* hasPermission */ true); + true /*hasPermission*/, ADISPLAY_ID_DEFAULT); window->consumeTouchModeEvent(true); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3424,8 +3615,8 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -3463,8 +3654,8 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -3568,9 +3759,9 @@ TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) { TEST_F(InputDispatcherTest, SetFocusedWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowTop = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> windowSecond = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); // Top window is also focusable but is not granted focus. @@ -3591,7 +3782,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow) { TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -3611,7 +3802,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) { TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); window->setFocusable(false); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -3629,9 +3820,9 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) { TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowTop = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> windowSecond = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); windowTop->setFocusable(true); @@ -3654,9 +3845,9 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) { TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowTop = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> windowSecond = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); windowTop->setFocusable(true); @@ -3675,10 +3866,10 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) { TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> previousFocusedWindow = - new FakeWindowHandle(application, mDispatcher, "previousFocusedWindow", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "previousFocusedWindow", + ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -3713,7 +3904,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) { TEST_F(InputDispatcherTest, DisplayRemoved) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); // window is granted focus. @@ -3758,7 +3949,7 @@ TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); sp<FakeWindowHandle> slipperyExitWindow = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); slipperyExitWindow->setSlippery(true); // Make sure this one overlaps the bottom window slipperyExitWindow->setFrame(Rect(25, 25, 75, 75)); @@ -3767,7 +3958,7 @@ TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) { slipperyExitWindow->setOwnerInfo(SLIPPERY_PID, SLIPPERY_UID); sp<FakeWindowHandle> slipperyEnterWindow = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); slipperyExitWindow->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows( @@ -3801,7 +3992,7 @@ protected: sp<FakeWindowHandle> mWindow; virtual void SetUp() override { - mFakePolicy = new FakeInputDispatcherPolicy(); + mFakePolicy = sp<FakeInputDispatcherPolicy>::make(); mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); @@ -3812,7 +4003,7 @@ protected: void setUpWindow() { mApp = std::make_shared<FakeApplicationHandle>(); - mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); @@ -3949,7 +4140,7 @@ public: application1 = std::make_shared<FakeApplicationHandle>(); windowInPrimary = - new FakeWindowHandle(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT); // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); @@ -3960,7 +4151,7 @@ public: application2 = std::make_shared<FakeApplicationHandle>(); windowInSecondary = - new FakeWindowHandle(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID); + sp<FakeWindowHandle>::make(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID); // Set focus to second display window. // Set focus display to second one. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); @@ -4089,7 +4280,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) { sp<FakeWindowHandle> secondWindowInPrimary = - new FakeWindowHandle(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); secondWindowInPrimary->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}}); setFocusedWindow(secondWindowInPrimary); @@ -4258,8 +4449,8 @@ protected: std::shared_ptr<InputApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - mWindow = - new FakeWindowHandle(application, mDispatcher, "Test Window", ADISPLAY_ID_DEFAULT); + mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Test Window", + ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); mWindow->setFocusable(true); @@ -4363,11 +4554,11 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); mUnfocusedWindow = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); mFocusedWindow = - new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); // Set focused application. @@ -4473,12 +4664,12 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", - ADISPLAY_ID_DEFAULT); + mWindow1 = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window 1", + ADISPLAY_ID_DEFAULT); mWindow1->setFrame(Rect(0, 0, 100, 100)); - mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", - ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2 = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window 2", + ADISPLAY_ID_DEFAULT, mWindow1->getToken()); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); @@ -4508,6 +4699,7 @@ protected: const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); assertMotionAction(expectedAction, motionEvent.getAction()); + ASSERT_EQ(points.size(), motionEvent.getPointerCount()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; @@ -4660,8 +4852,8 @@ class InputDispatcherSingleWindowAnr : public InputDispatcherTest { mApplication = std::make_shared<FakeApplicationHandle>(); mApplication->setDispatchingTimeout(20ms); - mWindow = - new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "TestWindow", + ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 30, 30)); mWindow->setDispatchingTimeout(30ms); mWindow->setFocusable(true); @@ -4695,7 +4887,7 @@ protected: sp<FakeWindowHandle> addSpyWindow() { sp<FakeWindowHandle> spy = - new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); spy->setTrustedOverlay(true); spy->setFocusable(false); spy->setSpy(true); @@ -5141,14 +5333,14 @@ class InputDispatcherMultiWindowAnr : public InputDispatcherTest { mApplication = std::make_shared<FakeApplicationHandle>(); mApplication->setDispatchingTimeout(10ms); - mUnfocusedWindow = - new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT); + mUnfocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Unfocused", + ADISPLAY_ID_DEFAULT); mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped mUnfocusedWindow->setWatchOutsideTouch(true); - mFocusedWindow = - new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); + mFocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Focused", + ADISPLAY_ID_DEFAULT); mFocusedWindow->setDispatchingTimeout(30ms); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); @@ -5515,16 +5707,16 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { InputDispatcherTest::SetUp(); mApplication = std::make_shared<FakeApplicationHandle>(); - mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher, - "Window without input channel", ADISPLAY_ID_DEFAULT, - std::make_optional<sp<IBinder>>(nullptr) /*token*/); - + mNoInputWindow = + sp<FakeWindowHandle>::make(mApplication, mDispatcher, + "Window without input channel", ADISPLAY_ID_DEFAULT, + std::make_optional<sp<IBinder>>(nullptr) /*token*/); mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); // It's perfectly valid for this window to not have an associated input channel - mBottomWindow = new FakeWindowHandle(mApplication, mDispatcher, "Bottom window", - ADISPLAY_ID_DEFAULT); + mBottomWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Bottom window", + ADISPLAY_ID_DEFAULT); mBottomWindow->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}}); @@ -5557,9 +5749,9 @@ TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouc */ TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouchesWithValidChannel) { - mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher, - "Window with input channel and NO_INPUT_CHANNEL", - ADISPLAY_ID_DEFAULT); + mNoInputWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, + "Window with input channel and NO_INPUT_CHANNEL", + ADISPLAY_ID_DEFAULT); mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); @@ -5585,9 +5777,9 @@ protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); - mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); - mMirror = new FakeWindowHandle(mApp, mDispatcher, "TestWindowMirror", ADISPLAY_ID_DEFAULT, - mWindow->getToken()); + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mMirror = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindowMirror", + ADISPLAY_ID_DEFAULT, mWindow->getToken()); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); mWindow->setFocusable(true); mMirror->setFocusable(true); @@ -5729,9 +5921,10 @@ protected: void SetUp() override { InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); - mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFocusable(true); - mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); + mSecondWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); mSecondWindow->setFocusable(true); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); @@ -5892,7 +6085,6 @@ protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); mTouchWindow = getWindow(TOUCHED_APP_UID, "Touched"); - mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK); mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY); } @@ -5913,7 +6105,7 @@ protected: sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) { std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(app, mDispatcher, name, ADISPLAY_ID_DEFAULT); // Generate an arbitrary PID based on the UID window->setOwnerInfo(1777 + (uid % 10000), uid); return window; @@ -6273,20 +6465,28 @@ protected: sp<FakeWindowHandle> mWindow; sp<FakeWindowHandle> mSecondWindow; sp<FakeWindowHandle> mDragWindow; + sp<FakeWindowHandle> mSpyWindow; // Mouse would force no-split, set the id as non-zero to verify if drag state could track it. static constexpr int32_t MOUSE_POINTER_ID = 1; void SetUp() override { InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); - mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 100, 100)); - mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); + mSecondWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); mSecondWindow->setFrame(Rect(100, 0, 200, 100)); + mSpyWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "SpyWindow", ADISPLAY_ID_DEFAULT); + mSpyWindow->setSpy(true); + mSpyWindow->setTrustedOverlay(true); + mSpyWindow->setFrame(Rect(0, 0, 200, 100)); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}}); } void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { @@ -6327,6 +6527,8 @@ protected: // Window should receive motion event. mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + // Spy window should also receive motion event + mSpyWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); } // Start performing drag, we will create a drag window and transfer touch to it. @@ -6338,9 +6540,11 @@ protected: } // The drag window covers the entire display - mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT); + mDragWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT); + mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}}); mDispatcher->setInputWindows( - {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}}); + {{ADISPLAY_ID_DEFAULT, {mDragWindow, mSpyWindow, mWindow, mSecondWindow}}}); // Transfer touch focus to the drag window bool transferred = @@ -6392,6 +6596,30 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) { + startDrag(); + + // No cancel event after drag start + mSpyWindow->assertNoEvents(); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(60).y(60)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Receives cancel for first pointer after next pointer down + mSpyWindow->consumeMotionCancel(); + mSpyWindow->consumeMotionDown(); + + mSpyWindow->assertNoEvents(); +} + TEST_F(InputDispatcherDragTests, DragAndDrop) { startDrag(); @@ -6598,7 +6826,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { // Update window of second display. sp<FakeWindowHandle> windowInSecondary = - new FakeWindowHandle(mApp, mDispatcher, "D_2", SECOND_DISPLAY_ID); + sp<FakeWindowHandle>::make(mApp, mDispatcher, "D_2", SECOND_DISPLAY_ID); mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}}); // Let second display has a touch state. @@ -6698,8 +6926,8 @@ class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {}; TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); window->setDropInput(true); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -6737,14 +6965,14 @@ TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { std::shared_ptr<FakeApplicationHandle> obscuringApplication = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> obscuringWindow = - new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); obscuringWindow->setFrame(Rect(0, 0, 50, 50)); obscuringWindow->setOwnerInfo(111, 111); obscuringWindow->setTouchable(false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); window->setDropInputIfObscured(true); window->setOwnerInfo(222, 222); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -6783,14 +7011,14 @@ TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { std::shared_ptr<FakeApplicationHandle> obscuringApplication = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> obscuringWindow = - new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); obscuringWindow->setFrame(Rect(0, 0, 50, 50)); obscuringWindow->setOwnerInfo(111, 111); obscuringWindow->setTouchable(false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Test window", ADISPLAY_ID_DEFAULT); window->setDropInputIfObscured(true); window->setOwnerInfo(222, 222); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -6827,42 +7055,67 @@ TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { class InputDispatcherTouchModeChangedTests : public InputDispatcherTest { protected: std::shared_ptr<FakeApplicationHandle> mApp; + std::shared_ptr<FakeApplicationHandle> mSecondaryApp; sp<FakeWindowHandle> mWindow; sp<FakeWindowHandle> mSecondWindow; + sp<FakeWindowHandle> mThirdWindow; void SetUp() override { InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); - mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mSecondaryApp = std::make_shared<FakeApplicationHandle>(); + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFocusable(true); setFocusedWindow(mWindow); - mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); + mSecondWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); mSecondWindow->setFocusable(true); + mThirdWindow = + sp<FakeWindowHandle>::make(mSecondaryApp, mDispatcher, + "TestWindow3_SecondaryDisplay", SECOND_DISPLAY_ID); + mThirdWindow->setFocusable(true); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}, + {SECOND_DISPLAY_ID, {mThirdWindow}}}); + mThirdWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID); mWindow->consumeFocusEvent(true); - // Set initial touch mode to InputDispatcher::kDefaultInTouchMode. + // Set main display initial touch mode to InputDispatcher::kDefaultInTouchMode. if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID, - WINDOW_UID, /* hasPermission */ true)) { + WINDOW_UID, true /* hasPermission */, + ADISPLAY_ID_DEFAULT)) { mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode); mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode); + mThirdWindow->assertNoEvents(); + } + + // Set secondary display initial touch mode to InputDispatcher::kDefaultInTouchMode. + if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, SECONDARY_WINDOW_PID, + SECONDARY_WINDOW_UID, true /* hasPermission */, + SECOND_DISPLAY_ID)) { + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mThirdWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode); } } - void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) { - ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission)); + void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, int32_t pid, int32_t uid, + bool hasPermission) { + ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission, + ADISPLAY_ID_DEFAULT)); mWindow->consumeTouchModeEvent(inTouchMode); mSecondWindow->consumeTouchModeEvent(inTouchMode); + mThirdWindow->assertNoEvents(); } }; TEST_F(InputDispatcherTouchModeChangedTests, FocusedWindowCanChangeTouchMode) { const WindowInfo& windowInfo = *mWindow->getInfo(); - changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid, - windowInfo.ownerUid, /* hasPermission */ false); + changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, + windowInfo.ownerPid, windowInfo.ownerUid, + false /* hasPermission */); } TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) { @@ -6871,7 +7124,8 @@ TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTo int32_t ownerUid = windowInfo.ownerUid; mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1); ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid, - ownerUid, /* hasPermission */ false)); + ownerUid, false /*hasPermission*/, + ADISPLAY_ID_DEFAULT)); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); } @@ -6881,19 +7135,29 @@ TEST_F(InputDispatcherTouchModeChangedTests, NonWindowOwnerMayChangeTouchModeOnP int32_t ownerPid = windowInfo.ownerPid; int32_t ownerUid = windowInfo.ownerUid; mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1); - changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, ownerPid, ownerUid, - /* hasPermission */ true); + changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, ownerPid, + ownerUid, true /*hasPermission*/); } TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) { const WindowInfo& windowInfo = *mWindow->getInfo(); ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid, windowInfo.ownerUid, - /* hasPermission */ true)); + true /*hasPermission*/, ADISPLAY_ID_DEFAULT)); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchOnSecondaryDisplayOnly) { + const WindowInfo& windowInfo = *mThirdWindow->getInfo(); + ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode, + windowInfo.ownerPid, windowInfo.ownerUid, + true /*hasPermission*/, SECOND_DISPLAY_ID)); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mThirdWindow->consumeTouchModeEvent(!InputDispatcher::kDefaultInTouchMode); +} + TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) { // Interact with the window first. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) @@ -6908,7 +7172,7 @@ TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInt const WindowInfo& windowInfo = *mWindow->getInfo(); ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid, windowInfo.ownerUid, - /* hasPermission= */ false)); + false /*hasPermission*/, ADISPLAY_ID_DEFAULT)); } class InputDispatcherSpyWindowTest : public InputDispatcherTest { @@ -6918,8 +7182,8 @@ public: std::make_shared<FakeApplicationHandle>(); std::string name = "Fake Spy "; name += std::to_string(mSpyCount++); - sp<FakeWindowHandle> spy = - new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(application, mDispatcher, + name.c_str(), ADISPLAY_ID_DEFAULT); spy->setSpy(true); spy->setTrustedOverlay(true); return spy; @@ -6929,7 +7193,8 @@ public: std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", + ADISPLAY_ID_DEFAULT); window->setFocusable(true); return window; } @@ -7013,8 +7278,8 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) { break; // epoll_wait timed out } for (int i = 0; i < nFds; i++) { - ASSERT_EQ(EPOLLIN, events[i].events); - eventOrder.push_back(events[i].data.u64); + ASSERT_EQ(static_cast<uint32_t>(EPOLLIN), events[i].events); + eventOrder.push_back(static_cast<size_t>(events[i].data.u64)); channels[i]->consumeMotionDown(); } } @@ -7097,10 +7362,146 @@ TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) { } /** - * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to - * any other windows - including other spy windows - will also be cancelled. + * Even when a spy window spans over multiple foreground windows, the spy should receive all + * pointers that are down within its bounds. + */ +TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) { + auto windowLeft = createForeground(); + windowLeft->setFrame({0, 0, 100, 200}); + auto windowRight = createForeground(); + windowRight->setFrame({100, 0, 200, 200}); + auto spy = createSpy(); + spy->setFrame({0, 0, 200, 200}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + windowLeft->consumeMotionDown(); + spy->consumeMotionDown(); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer( + PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + windowRight->consumeMotionDown(); + spy->consumeMotionPointerDown(1 /*pointerIndex*/); +} + +/** + * When the first pointer lands outside the spy window and the second pointer lands inside it, the + * the spy should receive the second pointer with ACTION_DOWN. + */ +TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { + auto window = createForeground(); + window->setFrame({0, 0, 200, 200}); + auto spyRight = createSpy(); + spyRight->setFrame({100, 0, 200, 200}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(); + spyRight->assertNoEvents(); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer( + PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionPointerDown(1 /*pointerIndex*/); + spyRight->consumeMotionDown(); +} + +/** + * The spy window should not be able to affect whether or not touches are split. Only the foreground + * windows should be allowed to control split touch. + */ +TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { + // This spy window prevents touch splitting. However, we still expect to split touches + // because a foreground window has not disabled splitting. + auto spy = createSpy(); + spy->setPreventSplitting(true); + + auto window = createForeground(); + window->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Second finger down on window, the window should receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionPointerDown(1 /* pointerIndex */); +} + +/** + * A spy window will usually be implemented as an un-focusable window. Verify that these windows + * do not receive key events. */ -TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { +TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) { + auto spy = createSpy(); + spy->setFocusable(false); + + auto window = createForeground(); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyDown(ADISPLAY_ID_NONE); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyUp(ADISPLAY_ID_NONE); + + spy->assertNoEvents(); +} + +using InputDispatcherPilferPointersTest = InputDispatcherSpyWindowTest; + +/** + * A spy window can pilfer pointers. When this happens, touch gestures used by the spy window that + * are currently sent to any other windows - including other spy windows - will also be cancelled. + */ +TEST_F(InputDispatcherPilferPointersTest, PilferPointers) { auto window = createForeground(); auto spy1 = createSpy(); auto spy2 = createSpy(); @@ -7133,7 +7534,7 @@ TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { * A spy window can pilfer pointers for a gesture even after the foreground window has been removed * in the middle of the gesture. */ -TEST_F(InputDispatcherSpyWindowTest, CanPilferAfterWindowIsRemovedMidStream) { +TEST_F(InputDispatcherPilferPointersTest, CanPilferAfterWindowIsRemovedMidStream) { auto window = createForeground(); auto spy = createSpy(); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); @@ -7158,7 +7559,7 @@ TEST_F(InputDispatcherSpyWindowTest, CanPilferAfterWindowIsRemovedMidStream) { * After a spy window pilfers pointers, new pointers that go down in its bounds should be sent to * the spy, but not to any other windows. */ -TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) { +TEST_F(InputDispatcherPilferPointersTest, ContinuesToReceiveGestureAfterPilfer) { auto spy = createSpy(); auto window = createForeground(); @@ -7214,136 +7615,154 @@ TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) { } /** - * Even when a spy window spans over multiple foreground windows, the spy should receive all - * pointers that are down within its bounds. + * After a spy window pilfers pointers, only the pointers used by the spy should be canceled */ -TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) { - auto windowLeft = createForeground(); - windowLeft->setFrame({0, 0, 100, 200}); - auto windowRight = createForeground(); - windowRight->setFrame({100, 0, 200, 200}); +TEST_F(InputDispatcherPilferPointersTest, PartiallyPilferRequiredPointers) { auto spy = createSpy(); - spy->setFrame({0, 0, 200, 200}); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}}); + spy->setFrame(Rect(0, 0, 100, 100)); + auto window = createForeground(); + window->setFrame(Rect(0, 0, 200, 200)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + // First finger down on the window only ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {50, 50})) + {150, 150})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - windowLeft->consumeMotionDown(); - spy->consumeMotionDown(); + window->consumeMotionDown(); + // Second finger down on the spy and window const MotionEvent secondFingerDownEvent = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer( - PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) .build(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - windowRight->consumeMotionDown(); - spy->consumeMotionPointerDown(1 /*pointerIndex*/); + spy->consumeMotionDown(); + window->consumeMotionPointerDown(1); + + // Third finger down on the spy and window + const MotionEvent thirdFingerDownEvent = + MotionEventBuilder(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) + .pointer(PointerBuilder(/* id */ 2, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionPointerDown(1); + window->consumeMotionPointerDown(2); + + // Spy window pilfers the pointers. + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + window->consumeMotionPointerUp(/* idx */ 2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED); + window->consumeMotionPointerUp(/* idx */ 1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED); + + spy->assertNoEvents(); + window->assertNoEvents(); } /** - * When the first pointer lands outside the spy window and the second pointer lands inside it, the - * the spy should receive the second pointer with ACTION_DOWN. + * After a spy window pilfers pointers, all pilfered pointers that have already been dispatched to + * other windows should be canceled. If this results in the cancellation of all pointers for some + * window, then that window should receive ACTION_CANCEL. */ -TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { +TEST_F(InputDispatcherPilferPointersTest, PilferAllRequiredPointers) { + auto spy = createSpy(); + spy->setFrame(Rect(0, 0, 100, 100)); auto window = createForeground(); - window->setFrame({0, 0, 200, 200}); - auto spyRight = createSpy(); - spyRight->setFrame({100, 0, 200, 200}); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}}); + window->setFrame(Rect(0, 0, 200, 200)); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down on both spy and window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {50, 50})) + {10, 10})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; window->consumeMotionDown(); - spyRight->assertNoEvents(); + spy->consumeMotionDown(); + // Second finger down on the spy and window const MotionEvent secondFingerDownEvent = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer( - PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) .build(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerDown(1 /*pointerIndex*/); - spyRight->consumeMotionDown(); + spy->consumeMotionPointerDown(1); + window->consumeMotionPointerDown(1); + + // Spy window pilfers the pointers. + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + window->consumeMotionCancel(); + + spy->assertNoEvents(); + window->assertNoEvents(); } /** - * The spy window should not be able to affect whether or not touches are split. Only the foreground - * windows should be allowed to control split touch. + * After a spy window pilfers pointers, new pointers that are not touching the spy window can still + * be sent to other windows */ -TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { - // This spy window prevents touch splitting. However, we still expect to split touches - // because a foreground window has not disabled splitting. +TEST_F(InputDispatcherPilferPointersTest, CanReceivePointersAfterPilfer) { auto spy = createSpy(); - spy->setPreventSplitting(true); - + spy->setFrame(Rect(0, 0, 100, 100)); auto window = createForeground(); - window->setFrame(Rect(0, 0, 100, 100)); + window->setFrame(Rect(0, 0, 200, 200)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); - // First finger down, no window touched. + // First finger down on both window and spy ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 200})) + {10, 10})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->assertNoEvents(); + window->consumeMotionDown(); + spy->consumeMotionDown(); - // Second finger down on window, the window should receive touch down. + // Spy window pilfers the pointers. + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + window->consumeMotionCancel(); + + // Second finger down on the window only const MotionEvent secondFingerDownEvent = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .displayId(ADISPLAY_ID_DEFAULT) .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) - .x(100) - .y(200)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) .build(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(); + window->assertNoEvents(); - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - spy->consumeMotionPointerDown(1 /* pointerIndex */); -} - -/** - * A spy window will usually be implemented as an un-focusable window. Verify that these windows - * do not receive key events. - */ -TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) { - auto spy = createSpy(); - spy->setFocusable(false); - - auto window = createForeground(); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); - setFocusedWindow(window); - window->consumeFocusEvent(true); - - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeKeyDown(ADISPLAY_ID_NONE); - - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) - << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeKeyUp(ADISPLAY_ID_NONE); - + // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line + spy->consumeMotionMove(); spy->assertNoEvents(); } @@ -7353,8 +7772,8 @@ public: std::shared_ptr<FakeApplicationHandle> overlayApplication = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> overlay = - new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(overlayApplication, mDispatcher, + "Stylus interceptor window", ADISPLAY_ID_DEFAULT); overlay->setFocusable(false); overlay->setOwnerInfo(111, 111); overlay->setTouchable(false); @@ -7364,8 +7783,8 @@ public: std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Application window", - ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle>::make(application, mDispatcher, "Application window", + ADISPLAY_ID_DEFAULT); window->setFocusable(true); window->setOwnerInfo(222, 222); @@ -7505,8 +7924,9 @@ struct User { sp<FakeWindowHandle> createWindow() const { std::shared_ptr<FakeApplicationHandle> overlayApplication = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(overlayApplication, mDispatcher, - "Owned Window", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(overlayApplication, mDispatcher, "Owned Window", + ADISPLAY_ID_DEFAULT); window->setOwnerInfo(mPid, mUid); return window; } diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp deleted file mode 100644 index 454e531af6..0000000000 --- a/services/inputflinger/tests/InputFlingerService_test.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2020 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 <BnInputFlingerQuery.h> -#include <IInputFlingerQuery.h> - -#include <android/os/BnInputFlinger.h> -#include <android/os/IInputFlinger.h> - -#include <binder/Binder.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/Parcel.h> -#include <binder/ProcessState.h> - -#include <input/Input.h> -#include <input/InputTransport.h> - -#include <gtest/gtest.h> -#include <inttypes.h> -#include <linux/uinput.h> -#include <log/log.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <chrono> -#include <thread> -#include <unordered_map> - -#define TAG "InputFlingerServiceTest" - -using android::gui::FocusRequest; -using android::os::BnInputFlinger; -using android::os::IInputFlinger; - -using std::chrono_literals::operator""ms; -using std::chrono_literals::operator""s; - -namespace android { - -static const String16 kTestServiceName = String16("InputFlingerService"); -static const String16 kQueryServiceName = String16("InputFlingerQueryService"); - -// --- InputFlingerServiceTest --- -class InputFlingerServiceTest : public testing::Test { -public: - void SetUp() override; - void TearDown() override; - -protected: - void InitializeInputFlinger(); - - sp<IInputFlinger> mService; - sp<IInputFlingerQuery> mQuery; - -private: - std::unique_ptr<InputChannel> mServerChannel, mClientChannel; - std::mutex mLock; -}; - - -class TestInputManager : public BnInputFlinger { -protected: - virtual ~TestInputManager(){}; - -public: - TestInputManager(){}; - - binder::Status getInputChannels(std::vector<::android::InputChannel>* channels); - - status_t dump(int fd, const Vector<String16>& args) override; - - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; - binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; - binder::Status setFocusedWindow(const FocusRequest&) override; - - void reset(); - -private: - mutable Mutex mLock; - std::vector<std::shared_ptr<InputChannel>> mInputChannels; -}; - -class TestInputQuery : public BnInputFlingerQuery { -public: - TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){}; - binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override; - binder::Status resetInputManager() override; - -private: - sp<android::TestInputManager> mManager; -}; - -binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) { - return mManager->getInputChannels(channels); -} - -binder::Status TestInputQuery::resetInputManager() { - mManager->reset(); - return binder::Status::ok(); -} - -binder::Status TestInputManager::createInputChannel(const std::string& name, - InputChannel* outChannel) { - AutoMutex _l(mLock); - std::unique_ptr<InputChannel> serverChannel; - std::unique_ptr<InputChannel> clientChannel; - InputChannel::openInputChannelPair(name, serverChannel, clientChannel); - - clientChannel->copyTo(*outChannel); - - mInputChannels.emplace_back(std::move(serverChannel)); - - return binder::Status::ok(); -} - -binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) { - AutoMutex _l(mLock); - - auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(), - [&](std::shared_ptr<InputChannel>& c) { - return c->getConnectionToken() == connectionToken; - }); - if (it != mInputChannels.end()) { - mInputChannels.erase(it); - } - - return binder::Status::ok(); -} - -status_t TestInputManager::dump(int fd, const Vector<String16>& args) { - std::string dump; - - dump += " InputFlinger dump\n"; - - ::write(fd, dump.c_str(), dump.size()); - return NO_ERROR; -} - -binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) { - channels->clear(); - for (std::shared_ptr<InputChannel>& channel : mInputChannels) { - channels->push_back(*channel); - } - return binder::Status::ok(); -} - -binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) { - return binder::Status::ok(); -} - -void TestInputManager::reset() { - mInputChannels.clear(); -} - -void InputFlingerServiceTest::SetUp() { - InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - InitializeInputFlinger(); -} - -void InputFlingerServiceTest::TearDown() { - mQuery->resetInputManager(); -} - -void InputFlingerServiceTest::InitializeInputFlinger() { - sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName)); - ASSERT_TRUE(input != nullptr); - mService = interface_cast<IInputFlinger>(input); - - input = defaultServiceManager()->waitForService(kQueryServiceName); - ASSERT_TRUE(input != nullptr); - mQuery = interface_cast<IInputFlingerQuery>(input); -} - -/** - * Test InputFlinger service interface createInputChannel - */ -TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) { - // Test that the unblocked file descriptor flag is kept across processes over binder - // transactions. - - InputChannel channel; - ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); - - const base::unique_fd& fd = channel.getFd(); - ASSERT_TRUE(fd.ok()); - - const int result = fcntl(fd, F_GETFL); - EXPECT_NE(result, -1); - EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK); -} - -TEST_F(InputFlingerServiceTest, CreateInputChannel) { - InputChannel channel; - ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); - - std::vector<::android::InputChannel> channels; - mQuery->getInputChannels(&channels); - ASSERT_EQ(channels.size(), 1UL); - EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken()); - - mService->removeInputChannel(channel.getConnectionToken()); - mQuery->getInputChannels(&channels); - EXPECT_EQ(channels.size(), 0UL); -} - -} // namespace android - -int main(int argc, char** argv) { - pid_t forkPid = fork(); - - if (forkPid == 0) { - // Server process - android::sp<android::TestInputManager> manager = new android::TestInputManager(); - android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager); - - android::defaultServiceManager()->addService(android::kTestServiceName, manager, - false /*allowIsolated*/); - android::defaultServiceManager()->addService(android::kQueryServiceName, query, - false /*allowIsolated*/); - android::ProcessState::self()->startThreadPool(); - android::IPCThreadState::self()->joinThreadPool(); - } else { - android::ProcessState::self()->startThreadPool(); - ::testing::InitGoogleTest(&argc, argv); - int result = RUN_ALL_TESTS(); - kill(forkPid, SIGKILL); - return result; - } - return 0; -} diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp new file mode 100644 index 0000000000..3cd7c1b0f2 --- /dev/null +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2022 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 "InputMapperTest.h" + +#include <InputReaderBase.h> +#include <gtest/gtest.h> +#include <ui/Rotation.h> + +namespace android { + +const char* InputMapperTest::DEVICE_NAME = "device"; +const char* InputMapperTest::DEVICE_LOCATION = "USB1"; +const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES = + ftl::Flags<InputDeviceClass>(0); // not needed for current tests + +void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) { + mFakeEventHub = std::make_unique<FakeEventHub>(); + mFakePolicy = sp<FakeInputReaderPolicy>::make(); + mFakeListener = std::make_unique<TestInputListener>(); + mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); + mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus); + // Consume the device reset notification generated when adding a new device. + mFakeListener->assertNotifyDeviceResetWasCalled(); +} + +void InputMapperTest::SetUp() { + SetUp(DEVICE_CLASSES); +} + +void InputMapperTest::TearDown() { + mFakeListener.reset(); + mFakePolicy.clear(); +} + +void InputMapperTest::addConfigurationProperty(const char* key, const char* value) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value); +} + +std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) { + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) { + mReader->requestRefreshConfiguration(changes); + mReader->loopOnce(); + } + std::list<NotifyArgs> out = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + // Loop the reader to flush the input listener queue. + for (const NotifyArgs& args : out) { + mFakeListener->notify(args); + } + mReader->loopOnce(); + return out; +} + +std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name, + const std::string& location, + int32_t eventHubId, + ftl::Flags<InputDeviceClass> classes, + int bus) { + InputDeviceIdentifier identifier; + identifier.name = name; + identifier.location = location; + identifier.bus = bus; + std::shared_ptr<InputDevice> device = + std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION, + identifier); + mReader->pushNextDevice(device); + mFakeEventHub->addDevice(eventHubId, name, classes, bus); + mReader->loopOnce(); + return device; +} + +void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, + ui::Rotation orientation, + const std::string& uniqueId, + std::optional<uint8_t> physicalPort, + ViewportType viewportType) { + mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true, + uniqueId, physicalPort, viewportType); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); +} + +void InputMapperTest::clearViewports() { + mFakePolicy->clearViewports(); +} + +std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, + int32_t type, int32_t code, int32_t value) { + RawEvent event; + event.when = when; + event.readTime = readTime; + event.deviceId = mapper.getDeviceContext().getEventHubId(); + event.type = type; + event.code = code; + event.value = value; + std::list<NotifyArgs> processArgList = mapper.process(&event); + for (const NotifyArgs& args : processArgList) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + return processArgList; +} + +void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) { + const auto resetArgs = mapper.reset(when); + for (const auto args : resetArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); +} + +std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) { + std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when); + for (const NotifyArgs& args : generatedArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + return generatedArgs; +} + +void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, + float min, float max, float flat, float fuzz) { + const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); + ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source; + ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source; + ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source; + ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source; + ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source; + ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source; + ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source; +} + +void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y, + float pressure, float size, float touchMajor, + float touchMinor, float toolMajor, float toolMinor, + float orientation, float distance, + float scaledAxisEpsilon) { + ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon); + ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon); + ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); + ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); + ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon); + ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon); + ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon); + ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon); + ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); + ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON); +} + +void InputMapperTest::assertPosition(const FakePointerController& controller, float x, float y) { + float actualX, actualY; + controller.getPosition(&actualX, &actualY); + ASSERT_NEAR(x, actualX, 1); + ASSERT_NEAR(y, actualY, 1); +} + +} // namespace android diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h new file mode 100644 index 0000000000..b3401c37e2 --- /dev/null +++ b/services/inputflinger/tests/InputMapperTest.h @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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 <list> +#include <memory> + +#include <InputDevice.h> +#include <InputMapper.h> +#include <NotifyArgs.h> +#include <ftl/flags.h> +#include <utils/StrongPointer.h> + +#include "FakeEventHub.h" +#include "FakeInputReaderPolicy.h" +#include "InstrumentedInputReader.h" +#include "TestConstants.h" +#include "TestInputListener.h" + +namespace android { + +class InputMapperTest : public testing::Test { +protected: + static const char* DEVICE_NAME; + static const char* DEVICE_LOCATION; + static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; + static constexpr int32_t DEVICE_GENERATION = 2; + static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0; + static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES; + static constexpr int32_t EVENTHUB_ID = 1; + + std::shared_ptr<FakeEventHub> mFakeEventHub; + sp<FakeInputReaderPolicy> mFakePolicy; + std::unique_ptr<TestInputListener> mFakeListener; + std::unique_ptr<InstrumentedInputReader> mReader; + std::shared_ptr<InputDevice> mDevice; + + virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0); + void SetUp() override; + void TearDown() override; + + void addConfigurationProperty(const char* key, const char* value); + std::list<NotifyArgs> configureDevice(uint32_t changes); + std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, + const std::string& location, int32_t eventHubId, + ftl::Flags<InputDeviceClass> classes, int bus = 0); + template <class T, typename... Args> + T& addMapperAndConfigure(Args... args) { + T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...); + configureDevice(0); + std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME); + resetArgList += mapper.reset(ARBITRARY_TIME); + // Loop the reader to flush the input listener queue. + for (const NotifyArgs& loopArgs : resetArgList) { + mFakeListener->notify(loopArgs); + } + mReader->loopOnce(); + return mapper; + } + + void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, + ui::Rotation orientation, const std::string& uniqueId, + std::optional<uint8_t> physicalPort, + ViewportType viewportType); + void clearViewports(); + std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, + int32_t code, int32_t value); + void resetMapper(InputMapper& mapper, nsecs_t when); + + std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when); + + static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, + float min, float max, float flat, float fuzz); + static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, + float size, float touchMajor, float touchMinor, float toolMajor, + float toolMinor, float orientation, float distance, + float scaledAxisEpsilon = 1.f); + static void assertPosition(const FakePointerController& controller, float x, float y); +}; + +} // namespace android diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputProcessorConverter_test.cpp index 81ef9b95f0..040c8da309 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputProcessorConverter_test.cpp @@ -24,7 +24,7 @@ using namespace aidl::android::hardware::input; namespace android { -// --- InputClassifierConverterTest --- +// --- InputProcessorConverterTest --- static NotifyMotionArgs generateBasicMotionArgs() { // Create a basic motion event for testing @@ -52,7 +52,7 @@ static NotifyMotionArgs generateBasicMotionArgs() { static float getMotionEventAxis(common::PointerCoords coords, common::Axis axis) { uint32_t index = BitSet64::getIndexOfBit(static_cast<uint64_t>(coords.bits), - static_cast<uint64_t>(axis)); + static_cast<uint64_t>(axis)); return coords.values[index]; } @@ -60,7 +60,7 @@ static float getMotionEventAxis(common::PointerCoords coords, common::Axis axis) * Check that coordinates get converted properly from the framework's PointerCoords * to the hidl PointerCoords in input::common. */ -TEST(InputClassifierConverterTest, PointerCoordsAxes) { +TEST(InputProcessorConverterTest, PointerCoordsAxes) { const NotifyMotionArgs motionArgs = generateBasicMotionArgs(); ASSERT_EQ(1, motionArgs.pointerCoords[0].getX()); ASSERT_EQ(2, motionArgs.pointerCoords[0].getY()); @@ -76,7 +76,7 @@ TEST(InputClassifierConverterTest, PointerCoordsAxes) { ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::Axis::SIZE), motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE)); ASSERT_EQ(BitSet64::count(motionArgs.pointerCoords[0].bits), - BitSet64::count(motionEvent.pointerCoords[0].bits)); + BitSet64::count(motionEvent.pointerCoords[0].bits)); } } // namespace android diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp index 3a7712727a..380001c83a 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputProcessor_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "../InputClassifier.h" +#include "../InputProcessor.h" #include <gtest/gtest.h> #include <gui/constants.h> @@ -31,7 +31,7 @@ using aidl::android::hardware::input::processor::IInputProcessor; namespace android { -// --- InputClassifierTest --- +// --- InputProcessorTest --- static NotifyMotionArgs generateBasicMotionArgs() { // Create a basic motion event for testing @@ -56,105 +56,104 @@ static NotifyMotionArgs generateBasicMotionArgs() { return motionArgs; } -class InputClassifierTest : public testing::Test { +class InputProcessorTest : public testing::Test { protected: TestInputListener mTestListener; - std::unique_ptr<InputClassifierInterface> mClassifier; + std::unique_ptr<InputProcessorInterface> mProcessor; - void SetUp() override { mClassifier = std::make_unique<InputClassifier>(mTestListener); } + void SetUp() override { mProcessor = std::make_unique<InputProcessor>(mTestListener); } }; /** - * Create a basic configuration change and send it to input classifier. + * Create a basic configuration change and send it to input processor. * Expect that the event is received by the next input stage, unmodified. */ -TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) { - // Create a basic configuration change and send to classifier - NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/); +TEST_F(InputProcessorTest, SendToNextStage_NotifyConfigurationChangedArgs) { + // Create a basic configuration change and send to processor + NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); - mClassifier->notifyConfigurationChanged(&args); + mProcessor->notifyConfigurationChanged(&args); NotifyConfigurationChangedArgs outArgs; ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } -TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) { - // Create a basic key event and send to classifier +TEST_F(InputProcessorTest, SendToNextStage_NotifyKeyArgs) { + // Create a basic key event and send to processor NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, AMETA_NONE, 6 /*downTime*/); - mClassifier->notifyKey(&args); + mProcessor->notifyKey(&args); NotifyKeyArgs outArgs; ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } - /** - * Create a basic motion event and send it to input classifier. + * Create a basic motion event and send it to input processor. * Expect that the event is received by the next input stage, unmodified. */ -TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) { +TEST_F(InputProcessorTest, SendToNextStage_NotifyMotionArgs) { NotifyMotionArgs motionArgs = generateBasicMotionArgs(); - mClassifier->notifyMotion(&motionArgs); + mProcessor->notifyMotion(&motionArgs); NotifyMotionArgs args; ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); ASSERT_EQ(motionArgs, args); } /** - * Create a basic switch event and send it to input classifier. + * Create a basic switch event and send it to input processor. * Expect that the event is received by the next input stage, unmodified. */ -TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) { - NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/, - 5/*switchMask*/); +TEST_F(InputProcessorTest, SendToNextStage_NotifySwitchArgs) { + NotifySwitchArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*policyFlags*/, 4 /*switchValues*/, + 5 /*switchMask*/); - mClassifier->notifySwitch(&args); + mProcessor->notifySwitch(&args); NotifySwitchArgs outArgs; ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } /** - * Create a basic device reset event and send it to input classifier. + * Create a basic device reset event and send it to input processor. * Expect that the event is received by the next input stage, unmodified. */ -TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { - NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/); +TEST_F(InputProcessorTest, SendToNextStage_NotifyDeviceResetArgs) { + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*deviceId*/); - mClassifier->notifyDeviceReset(&args); + mProcessor->notifyDeviceReset(&args); NotifyDeviceResetArgs outArgs; ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } -TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) { - mClassifier->setMotionClassifierEnabled(true); +TEST_F(InputProcessorTest, SetMotionClassifier_Enabled) { + mProcessor->setMotionClassifierEnabled(true); } -TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) { - mClassifier->setMotionClassifierEnabled(false); +TEST_F(InputProcessorTest, SetMotionClassifier_Disabled) { + mProcessor->setMotionClassifierEnabled(false); } /** * Try to break it by calling setMotionClassifierEnabled multiple times. */ -TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) { - mClassifier->setMotionClassifierEnabled(true); - mClassifier->setMotionClassifierEnabled(true); - mClassifier->setMotionClassifierEnabled(true); - mClassifier->setMotionClassifierEnabled(false); - mClassifier->setMotionClassifierEnabled(false); - mClassifier->setMotionClassifierEnabled(true); - mClassifier->setMotionClassifierEnabled(true); - mClassifier->setMotionClassifierEnabled(true); +TEST_F(InputProcessorTest, SetMotionClassifier_Multiple) { + mProcessor->setMotionClassifierEnabled(true); + mProcessor->setMotionClassifierEnabled(true); + mProcessor->setMotionClassifierEnabled(true); + mProcessor->setMotionClassifierEnabled(false); + mProcessor->setMotionClassifierEnabled(false); + mProcessor->setMotionClassifierEnabled(true); + mProcessor->setMotionClassifierEnabled(true); + mProcessor->setMotionClassifierEnabled(true); } /** - * A minimal implementation of IInputClassifier. + * A minimal implementation of IInputProcessor. */ class TestHal : public aidl::android::hardware::input::processor::BnInputProcessor { ::ndk::ScopedAStatus classify( @@ -212,7 +211,7 @@ TEST_F(MotionClassifierTest, Classify_OneVideoFrame) { NotifyMotionArgs motionArgs = generateBasicMotionArgs(); std::vector<int16_t> videoData = {1, 2, 3, 4}; - timeval timestamp = { 1, 1}; + timeval timestamp = {1, 1}; TouchVideoFrame frame(2, 2, std::move(videoData), timestamp); motionArgs.videoFrames = {frame}; @@ -228,11 +227,11 @@ TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) { NotifyMotionArgs motionArgs = generateBasicMotionArgs(); std::vector<int16_t> videoData1 = {1, 2, 3, 4}; - timeval timestamp1 = { 1, 1}; + timeval timestamp1 = {1, 1}; TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1); std::vector<int16_t> videoData2 = {6, 6, 6, 6}; - timeval timestamp2 = { 1, 2}; + timeval timestamp2 = {1, 2}; TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2); motionArgs.videoFrames = {frame1, frame2}; @@ -253,7 +252,7 @@ TEST_F(MotionClassifierTest, Reset_DoesNotCrash) { * Make sure MotionClassifier does not crash when a device is reset. */ TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) { - NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/); + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*deviceId*/); ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args)); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 03fbf07b16..4cc48f6fc3 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -31,29 +31,35 @@ #include <SingleTouchInputMapper.h> #include <SwitchInputMapper.h> #include <TestInputListener.h> +#include <TestInputListenerMatchers.h> #include <TouchInputMapper.h> #include <UinputDevice.h> #include <VibratorInputMapper.h> #include <android-base/thread_annotations.h> +#include <ftl/enum.h> #include <gtest/gtest.h> #include <gui/constants.h> - +#include <ui/Rotation.h> + +#include <thread> +#include "FakeEventHub.h" +#include "FakeInputReaderPolicy.h" +#include "FakePointerController.h" +#include "InputMapperTest.h" +#include "InstrumentedInputReader.h" +#include "TestConstants.h" +#include "android/hardware/input/InputDeviceCountryCode.h" #include "input/DisplayViewport.h" #include "input/Input.h" +using android::hardware::input::InputDeviceCountryCode; + namespace android { using namespace ftl::flag_operators; - +using testing::AllOf; using std::chrono_literals::operator""ms; -// Timeout for waiting for an expected event -static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; - -// An arbitrary time value. -static constexpr nsecs_t ARBITRARY_TIME = 1234; -static constexpr nsecs_t READ_TIME = 4321; - // Arbitrary display properties. static constexpr int32_t DISPLAY_ID = 0; static const std::string DISPLAY_UNIQUE_ID = "local:1"; @@ -74,9 +80,6 @@ static constexpr int32_t INVALID_TRACKING_ID = -1; static constexpr int32_t FIRST_TRACKING_ID = 0; static constexpr int32_t SECOND_TRACKING_ID = 1; static constexpr int32_t THIRD_TRACKING_ID = 2; -static constexpr int32_t DEFAULT_BATTERY = 1; -static constexpr int32_t BATTERY_STATUS = 4; -static constexpr int32_t BATTERY_CAPACITY = 66; static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000; static constexpr int32_t LIGHT_COLOR = 0x7F448866; static constexpr int32_t LIGHT_PLAYER_ID = 2; @@ -90,33 +93,10 @@ static constexpr int32_t ACTION_POINTER_1_DOWN = static constexpr int32_t ACTION_POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -// Error tolerance for floating point assertions. -static const float EPSILON = 0.001f; - -using ::testing::AllOf; - -MATCHER_P(WithAction, action, "InputEvent with specified action") { - return arg.action == action; -} - -MATCHER_P(WithSource, source, "InputEvent with specified source") { - return arg.source == source; -} - -MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { - return arg.displayId == displayId; -} - -MATCHER_P2(WithCoords, x, y, "MotionEvent with specified action") { - return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY(); -} - -MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") { - const auto argToolType = arg.pointerProperties[0].toolType; - *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got " - << motionToolTypeToString(argToolType); - return argToolType == toolType; -} +// Minimum timestamp separation between subsequent input events from a Bluetooth device. +static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); +// Maximum smoothing time delta so that we don't generate events too far into the future. +constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); template<typename T> static inline T min(T a, T b) { @@ -132,12 +112,12 @@ const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightC {"green", LightColor::GREEN}, {"blue", LightColor::BLUE}}; -static int32_t getInverseRotation(int32_t orientation) { +static ui::Rotation getInverseRotation(ui::Rotation orientation) { switch (orientation) { - case DISPLAY_ORIENTATION_90: - return DISPLAY_ORIENTATION_270; - case DISPLAY_ORIENTATION_270: - return DISPLAY_ORIENTATION_90; + case ui::ROTATION_90: + return ui::ROTATION_270; + case ui::ROTATION_270: + return ui::ROTATION_90; default: return orientation; } @@ -161,921 +141,15 @@ static void assertAxisNotPresent(MultiTouchInputMapper& mapper, int axis) { ASSERT_EQ(nullptr, motionRange); } -// --- FakePointerController --- - -class FakePointerController : public PointerControllerInterface { - bool mHaveBounds; - float mMinX, mMinY, mMaxX, mMaxY; - float mX, mY; - int32_t mButtonState; - int32_t mDisplayId; - -public: - FakePointerController() : - mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0), - mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) { - } - - virtual ~FakePointerController() {} - - void setBounds(float minX, float minY, float maxX, float maxY) { - mHaveBounds = true; - mMinX = minX; - mMinY = minY; - mMaxX = maxX; - mMaxY = maxY; - } - - void setPosition(float x, float y) override { - mX = x; - mY = y; - } - - void setButtonState(int32_t buttonState) override { mButtonState = buttonState; } - - int32_t getButtonState() const override { return mButtonState; } - - void getPosition(float* outX, float* outY) const override { - *outX = mX; - *outY = mY; - } - - int32_t getDisplayId() const override { return mDisplayId; } - - void setDisplayViewport(const DisplayViewport& viewport) override { - mDisplayId = viewport.displayId; - } - - const std::map<int32_t, std::vector<int32_t>>& getSpots() { - return mSpotsByDisplay; - } - -private: - bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override { - *outMinX = mMinX; - *outMinY = mMinY; - *outMaxX = mMaxX; - *outMaxY = mMaxY; - return mHaveBounds; - } - - void move(float deltaX, float deltaY) override { - mX += deltaX; - if (mX < mMinX) mX = mMinX; - if (mX > mMaxX) mX = mMaxX; - mY += deltaY; - if (mY < mMinY) mY = mMinY; - if (mY > mMaxY) mY = mMaxY; - } - - void fade(Transition) override {} - - void unfade(Transition) override {} - - void setPresentation(Presentation) override {} - - void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, - int32_t displayId) override { - std::vector<int32_t> newSpots; - // Add spots for fingers that are down. - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - newSpots.push_back(id); - } - - mSpotsByDisplay[displayId] = newSpots; - } - - void clearSpots() override { mSpotsByDisplay.clear(); } - - std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; -}; - - -// --- FakeInputReaderPolicy --- - -class FakeInputReaderPolicy : public InputReaderPolicyInterface { - std::mutex mLock; - std::condition_variable mDevicesChangedCondition; - - InputReaderConfiguration mConfig; - std::shared_ptr<FakePointerController> mPointerController; - std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock); - bool mInputDevicesChanged GUARDED_BY(mLock){false}; - std::vector<DisplayViewport> mViewports; - TouchAffineTransformation transform; - -protected: - virtual ~FakeInputReaderPolicy() {} - -public: - FakeInputReaderPolicy() { - } - - void assertInputDevicesChanged() { - waitForInputDevices([](bool devicesChanged) { - if (!devicesChanged) { - FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; - } - }); - } - - void assertInputDevicesNotChanged() { - waitForInputDevices([](bool devicesChanged) { - if (devicesChanged) { - FAIL() << "Expected notifyInputDevicesChanged() to not be called."; - } - }); - } - - virtual void clearViewports() { - mViewports.clear(); - mConfig.setDisplayViewports(mViewports); - } - - std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const { - return mConfig.getDisplayViewportByUniqueId(uniqueId); - } - std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const { - return mConfig.getDisplayViewportByType(type); - } - - std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const { - return mConfig.getDisplayViewportByPort(displayPort); - } - - void addDisplayViewport(DisplayViewport viewport) { - mViewports.push_back(std::move(viewport)); - mConfig.setDisplayViewports(mViewports); - } - - void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, - bool isActive, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType type) { - const bool isRotated = - (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270); - DisplayViewport v; - v.displayId = displayId; - v.orientation = orientation; - v.logicalLeft = 0; - v.logicalTop = 0; - v.logicalRight = isRotated ? height : width; - v.logicalBottom = isRotated ? width : height; - v.physicalLeft = 0; - v.physicalTop = 0; - v.physicalRight = isRotated ? height : width; - v.physicalBottom = isRotated ? width : height; - v.deviceWidth = isRotated ? height : width; - v.deviceHeight = isRotated ? width : height; - v.isActive = isActive; - v.uniqueId = uniqueId; - v.physicalPort = physicalPort; - v.type = type; - - addDisplayViewport(v); - } - - bool updateViewport(const DisplayViewport& viewport) { - size_t count = mViewports.size(); - for (size_t i = 0; i < count; i++) { - const DisplayViewport& currentViewport = mViewports[i]; - if (currentViewport.displayId == viewport.displayId) { - mViewports[i] = viewport; - mConfig.setDisplayViewports(mViewports); - return true; - } - } - // no viewport found. - return false; - } - - void addExcludedDeviceName(const std::string& deviceName) { - mConfig.excludedDeviceNames.push_back(deviceName); - } - - void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) { - mConfig.portAssociations.insert({inputPort, displayPort}); - } - - void addInputUniqueIdAssociation(const std::string& inputUniqueId, - const std::string& displayUniqueId) { - mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId}); - } - - void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } - - void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } - - void setPointerController(std::shared_ptr<FakePointerController> controller) { - mPointerController = std::move(controller); - } - - const InputReaderConfiguration* getReaderConfiguration() const { - return &mConfig; - } - - const std::vector<InputDeviceInfo>& getInputDevices() const { - return mInputDevices; - } - - TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, - int32_t surfaceRotation) { - return transform; - } - - void setTouchAffineTransformation(const TouchAffineTransformation t) { - transform = t; - } - - PointerCaptureRequest setPointerCapture(bool enabled) { - mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++}; - return mConfig.pointerCaptureRequest; - } - - void setShowTouches(bool enabled) { - mConfig.showTouches = enabled; - } - - void setDefaultPointerDisplayId(int32_t pointerDisplayId) { - mConfig.defaultPointerDisplayId = pointerDisplayId; - } - - float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; } - - void setVelocityControlParams(const VelocityControlParameters& params) { - mConfig.pointerVelocityControlParameters = params; - mConfig.wheelVelocityControlParameters = params; - } - -private: - uint32_t mNextPointerCaptureSequenceNumber = 0; - - void getReaderConfiguration(InputReaderConfiguration* outConfig) override { - *outConfig = mConfig; - } - - std::shared_ptr<PointerControllerInterface> obtainPointerController( - int32_t /*deviceId*/) override { - return mPointerController; - } - - void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override { - std::scoped_lock<std::mutex> lock(mLock); - mInputDevices = inputDevices; - mInputDevicesChanged = true; - mDevicesChangedCondition.notify_all(); - } - - std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( - const InputDeviceIdentifier&) override { - return nullptr; - } - - std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; } - - void waitForInputDevices(std::function<void(bool)> processDevicesChanged) { - std::unique_lock<std::mutex> lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - const bool devicesChanged = - mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { - return mInputDevicesChanged; - }); - ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged)); - mInputDevicesChanged = false; - } -}; - -// --- FakeEventHub --- - -class FakeEventHub : public EventHubInterface { - struct KeyInfo { - int32_t keyCode; - uint32_t flags; - }; - - struct SensorInfo { - InputDeviceSensorType sensorType; - int32_t sensorDataIndex; - }; - - struct Device { - InputDeviceIdentifier identifier; - ftl::Flags<InputDeviceClass> classes; - PropertyMap configuration; - KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes; - KeyedVector<int, bool> relativeAxes; - KeyedVector<int32_t, int32_t> keyCodeStates; - KeyedVector<int32_t, int32_t> scanCodeStates; - KeyedVector<int32_t, int32_t> switchStates; - KeyedVector<int32_t, int32_t> absoluteAxisValue; - KeyedVector<int32_t, KeyInfo> keysByScanCode; - KeyedVector<int32_t, KeyInfo> keysByUsageCode; - KeyedVector<int32_t, bool> leds; - // fake mapping which would normally come from keyCharacterMap - std::unordered_map<int32_t, int32_t> keyCodeMapping; - std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode; - BitArray<MSC_MAX> mscBitmask; - std::vector<VirtualKeyDefinition> virtualKeys; - bool enabled; - - status_t enable() { - enabled = true; - return OK; - } - - status_t disable() { - enabled = false; - return OK; - } - - explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {} - }; - - std::mutex mLock; - std::condition_variable mEventsCondition; - - KeyedVector<int32_t, Device*> mDevices; - std::vector<std::string> mExcludedDevices; - std::vector<RawEvent> mEvents GUARDED_BY(mLock); - std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; - std::vector<int32_t> mVibrators = {0, 1}; - std::unordered_map<int32_t, RawLightInfo> mRawLightInfos; - // Simulates a device light brightness, from light id to light brightness. - std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness; - // Simulates a device light intensities, from light id to light intensities map. - std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>> - mLightIntensities; - -public: - virtual ~FakeEventHub() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } - } - - FakeEventHub() { } - - void addDevice(int32_t deviceId, const std::string& name, - ftl::Flags<InputDeviceClass> classes) { - Device* device = new Device(classes); - device->identifier.name = name; - mDevices.add(deviceId, device); - - enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0); - } - - void removeDevice(int32_t deviceId) { - delete mDevices.valueFor(deviceId); - mDevices.removeItem(deviceId); - - enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0); - } - - bool isDeviceEnabled(int32_t deviceId) { - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return false; - } - return device->enabled; - } - - status_t enableDevice(int32_t deviceId) { - status_t result; - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (device->enabled) { - ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId); - return OK; - } - result = device->enable(); - return result; - } - - status_t disableDevice(int32_t deviceId) { - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (!device->enabled) { - ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId); - return OK; - } - return device->disable(); - } - - void finishDeviceScan() { - enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0); - } - - void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) { - Device* device = getDevice(deviceId); - device->configuration.addProperty(key, value); - } - - void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) { - Device* device = getDevice(deviceId); - device->configuration.addAll(configuration); - } - - void addAbsoluteAxis(int32_t deviceId, int axis, - int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) { - Device* device = getDevice(deviceId); - - RawAbsoluteAxisInfo info; - info.valid = true; - info.minValue = minValue; - info.maxValue = maxValue; - info.flat = flat; - info.fuzz = fuzz; - info.resolution = resolution; - device->absoluteAxes.add(axis, info); - } - - void addRelativeAxis(int32_t deviceId, int32_t axis) { - Device* device = getDevice(deviceId); - device->relativeAxes.add(axis, true); - } - - void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) { - Device* device = getDevice(deviceId); - device->keyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { - Device* device = getDevice(deviceId); - device->scanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) { - Device* device = getDevice(deviceId); - device->switchStates.replaceValueFor(switchCode, state); - } - - void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) { - Device* device = getDevice(deviceId); - device->absoluteAxisValue.replaceValueFor(axis, value); - } - - void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, - int32_t keyCode, uint32_t flags) { - Device* device = getDevice(deviceId); - KeyInfo info; - info.keyCode = keyCode; - info.flags = flags; - if (scanCode) { - device->keysByScanCode.add(scanCode, info); - } - if (usageCode) { - device->keysByUsageCode.add(usageCode, info); - } - } - - void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) { - Device* device = getDevice(deviceId); - device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); - } - - void addLed(int32_t deviceId, int32_t led, bool initialState) { - Device* device = getDevice(deviceId); - device->leds.add(led, initialState); - } - - void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, - int32_t sensorDataIndex) { - Device* device = getDevice(deviceId); - SensorInfo info; - info.sensorType = sensorType; - info.sensorDataIndex = sensorDataIndex; - device->sensorsByAbsCode.emplace(absCode, info); - } - - void setMscEvent(int32_t deviceId, int32_t mscEvent) { - Device* device = getDevice(deviceId); - typename BitArray<MSC_MAX>::Buffer buffer; - buffer[mscEvent / 32] = 1 << mscEvent % 32; - device->mscBitmask.loadFromBuffer(buffer); - } - - void addRawLightInfo(int32_t rawId, RawLightInfo&& info) { - mRawLightInfos.emplace(rawId, std::move(info)); - } - - void fakeLightBrightness(int32_t rawId, int32_t brightness) { - mLightBrightness.emplace(rawId, brightness); - } - - void fakeLightIntensities(int32_t rawId, - const std::unordered_map<LightColor, int32_t> intensities) { - mLightIntensities.emplace(rawId, std::move(intensities)); - } - - bool getLedState(int32_t deviceId, int32_t led) { - Device* device = getDevice(deviceId); - return device->leds.valueFor(led); - } - - std::vector<std::string>& getExcludedDevices() { - return mExcludedDevices; - } - - void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) { - Device* device = getDevice(deviceId); - device->virtualKeys.push_back(definition); - } - - void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code, - int32_t value) { - std::scoped_lock<std::mutex> lock(mLock); - RawEvent event; - event.when = when; - event.readTime = readTime; - event.deviceId = deviceId; - event.type = type; - event.code = code; - event.value = value; - mEvents.push_back(event); - - if (type == EV_ABS) { - setAbsoluteAxisValue(deviceId, code, value); - } - } - - void setVideoFrames(std::unordered_map<int32_t /*deviceId*/, - std::vector<TouchVideoFrame>> videoFrames) { - mVideoFrames = std::move(videoFrames); - } - - void assertQueueIsEmpty() { - std::unique_lock<std::mutex> lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - const bool queueIsEmpty = - mEventsCondition.wait_for(lock, WAIT_TIMEOUT, - [this]() REQUIRES(mLock) { return mEvents.size() == 0; }); - if (!queueIsEmpty) { - FAIL() << "Timed out waiting for EventHub queue to be emptied."; - } - } - -private: - Device* getDevice(int32_t deviceId) const { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : nullptr; - } - - ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override { - Device* device = getDevice(deviceId); - return device ? device->classes : ftl::Flags<InputDeviceClass>(0); - } - - InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override { - Device* device = getDevice(deviceId); - return device ? device->identifier : InputDeviceIdentifier(); - } - - int32_t getDeviceControllerNumber(int32_t) const override { return 0; } - - void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override { - Device* device = getDevice(deviceId); - if (device) { - *outConfiguration = device->configuration; - } - } - - status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override { - Device* device = getDevice(deviceId); - if (device && device->enabled) { - ssize_t index = device->absoluteAxes.indexOfKey(axis); - if (index >= 0) { - *outAxisInfo = device->absoluteAxes.valueAt(index); - return OK; - } - } - outAxisInfo->clear(); - return -1; - } - - bool hasRelativeAxis(int32_t deviceId, int axis) const override { - Device* device = getDevice(deviceId); - if (device) { - return device->relativeAxes.indexOfKey(axis) >= 0; - } - return false; - } - - bool hasInputProperty(int32_t, int) const override { return false; } - - bool hasMscEvent(int32_t deviceId, int mscEvent) const override final { - Device* device = getDevice(deviceId); - if (device) { - return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false; - } - return false; - } - - status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override { - Device* device = getDevice(deviceId); - if (device) { - const KeyInfo* key = getKey(device, scanCode, usageCode); - if (key) { - if (outKeycode) { - *outKeycode = key->keyCode; - } - if (outFlags) { - *outFlags = key->flags; - } - if (outMetaState) { - *outMetaState = metaState; - } - return OK; - } - } - return NAME_NOT_FOUND; - } - - const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const { - if (usageCode) { - ssize_t index = device->keysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &device->keysByUsageCode.valueAt(index); - } - } - if (scanCode) { - ssize_t index = device->keysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &device->keysByScanCode.valueAt(index); - } - } - return nullptr; - } - - status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; } - - base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId, - int32_t absCode) { - Device* device = getDevice(deviceId); - if (!device) { - return Errorf("Sensor device not found."); - } - auto it = device->sensorsByAbsCode.find(absCode); - if (it == device->sensorsByAbsCode.end()) { - return Errorf("Sensor map not found."); - } - const SensorInfo& info = it->second; - return std::make_pair(info.sensorType, info.sensorDataIndex); - } - - void setExcludedDevices(const std::vector<std::string>& devices) override { - mExcludedDevices = devices; - } - - size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override { - std::scoped_lock lock(mLock); - - const size_t filledSize = std::min(mEvents.size(), bufferSize); - std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer); - - mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize); - mEventsCondition.notify_all(); - return filledSize; - } - - std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { - auto it = mVideoFrames.find(deviceId); - if (it != mVideoFrames.end()) { - std::vector<TouchVideoFrame> frames = std::move(it->second); - mVideoFrames.erase(deviceId); - return frames; - } - return {}; - } - - int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->scanCodeStates.indexOfKey(scanCode); - if (index >= 0) { - return device->scanCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keyCodeStates.indexOfKey(keyCode); - if (index >= 0) { - return device->keyCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->switchStates.indexOfKey(sw); - if (index >= 0) { - return device->switchStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->absoluteAxisValue.indexOfKey(axis); - if (index >= 0) { - *outValue = device->absoluteAxisValue.valueAt(index); - return OK; - } - } - *outValue = 0; - return -1; - } - - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { - Device* device = getDevice(deviceId); - if (!device) { - return AKEYCODE_UNKNOWN; - } - auto it = device->keyCodeMapping.find(locationKeyCode); - return it != device->keyCodeMapping.end() ? it->second : locationKeyCode; - } - - // Return true if the device has non-empty key layout. - bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const override { - bool result = false; - Device* device = getDevice(deviceId); - if (device) { - result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0; - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < device->keysByScanCode.size(); j++) { - if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) { - outFlags[i] = 1; - } - } - for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { - if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) { - outFlags[i] = 1; - } - } - } - } - return result; - } - - bool hasScanCode(int32_t deviceId, int32_t scanCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keysByScanCode.indexOfKey(scanCode); - return index >= 0; - } - return false; - } - - bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override { - Device* device = getDevice(deviceId); - if (!device) { - return false; - } - for (size_t i = 0; i < device->keysByScanCode.size(); i++) { - if (keyCode == device->keysByScanCode.valueAt(i).keyCode) { - return true; - } - } - for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { - if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) { - return true; - } - } - return false; - } - - bool hasLed(int32_t deviceId, int32_t led) const override { - Device* device = getDevice(deviceId); - return device && device->leds.indexOfKey(led) >= 0; - } - - void setLedState(int32_t deviceId, int32_t led, bool on) override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->leds.indexOfKey(led); - if (index >= 0) { - device->leds.replaceValueAt(led, on); - } else { - ADD_FAILURE() - << "Attempted to set the state of an LED that the EventHub declared " - "was not present. led=" << led; - } - } - } - - void getVirtualKeyDefinitions( - int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override { - outVirtualKeys.clear(); - - Device* device = getDevice(deviceId); - if (device) { - outVirtualKeys = device->virtualKeys; - } - } - - const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override { - return nullptr; - } - - bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override { - return false; - } - - void vibrate(int32_t, const VibrationElement&) override {} - - void cancelVibrate(int32_t) override {} - - std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; }; - - std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override { - return BATTERY_CAPACITY; - } - - std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override { - return BATTERY_STATUS; +[[maybe_unused]] static void dumpReader(InputReader& reader) { + std::string dump; + reader.dump(dump); + std::istringstream iss(dump); + for (std::string line; std::getline(iss, line);) { + ALOGE("%s", line.c_str()); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - - const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override { - return {DEFAULT_BATTERY}; - } - - std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) { - return std::nullopt; - } - - const std::vector<int32_t> getRawLightIds(int32_t deviceId) override { - std::vector<int32_t> ids; - for (const auto& [rawId, info] : mRawLightInfos) { - ids.push_back(rawId); - } - return ids; - } - - std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override { - auto it = mRawLightInfos.find(lightId); - if (it == mRawLightInfos.end()) { - return std::nullopt; - } - return it->second; - } - - void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override { - mLightBrightness.emplace(lightId, brightness); - } - - void setLightIntensities(int32_t deviceId, int32_t lightId, - std::unordered_map<LightColor, int32_t> intensities) override { - mLightIntensities.emplace(lightId, intensities); - }; - - std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override { - auto lightIt = mLightBrightness.find(lightId); - if (lightIt == mLightBrightness.end()) { - return std::nullopt; - } - return lightIt->second; - } - - std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( - int32_t deviceId, int32_t lightId) override { - auto lightIt = mLightIntensities.find(lightId); - if (lightIt == mLightIntensities.end()) { - return std::nullopt; - } - return lightIt->second; - }; - - virtual bool isExternal(int32_t) const { - return false; - } - - void dump(std::string&) override {} - - void monitor() override {} - - void requestReopenDevices() override {} - - void wake() override {} -}; +} // --- FakeInputMapper --- @@ -1191,7 +265,8 @@ private: } } - void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override { + std::list<NotifyArgs> configure(nsecs_t, const InputReaderConfiguration* config, + uint32_t changes) override { std::scoped_lock<std::mutex> lock(mLock); mConfigureWasCalled = true; @@ -1202,19 +277,22 @@ private: } mStateChangedCondition.notify_all(); + return {}; } - void reset(nsecs_t) override { + std::list<NotifyArgs> reset(nsecs_t) override { std::scoped_lock<std::mutex> lock(mLock); mResetWasCalled = true; mStateChangedCondition.notify_all(); + return {}; } - void process(const RawEvent* rawEvent) override { + std::list<NotifyArgs> process(const RawEvent* rawEvent) override { std::scoped_lock<std::mutex> lock(mLock); mLastEvent = *rawEvent; mProcessWasCalled = true; mStateChangedCondition.notify_all(); + return {}; } int32_t getKeyCodeState(uint32_t, int32_t keyCode) override { @@ -1238,9 +316,9 @@ private: } // Return true if the device has non-empty key layout. - bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes, + bool markSupportedKeyCodes(uint32_t, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) override { - for (size_t i = 0; i < numCodes; i++) { + for (size_t i = 0; i < keyCodes.size(); i++) { for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) { if (keyCodes[i] == mSupportedKeyCodes[j]) { outFlags[i] = 1; @@ -1266,98 +344,12 @@ private: } }; - -// --- InstrumentedInputReader --- - -class InstrumentedInputReader : public InputReader { - std::queue<std::shared_ptr<InputDevice>> mNextDevices; - -public: - InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, - const sp<InputReaderPolicyInterface>& policy, - InputListenerInterface& listener) - : InputReader(eventHub, policy, listener), mFakeContext(this) {} - - virtual ~InstrumentedInputReader() {} - - void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); } - - std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, - const std::string& location = "") { - InputDeviceIdentifier identifier; - identifier.name = name; - identifier.location = location; - int32_t generation = deviceId + 1; - return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier); - } - - // Make the protected loopOnce method accessible to tests. - using InputReader::loopOnce; - -protected: - virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId, - const InputDeviceIdentifier& identifier) - REQUIRES(mLock) { - if (!mNextDevices.empty()) { - std::shared_ptr<InputDevice> device(std::move(mNextDevices.front())); - mNextDevices.pop(); - return device; - } - return InputReader::createDeviceLocked(eventHubId, identifier); - } - - // --- FakeInputReaderContext --- - class FakeInputReaderContext : public ContextImpl { - int32_t mGlobalMetaState; - bool mUpdateGlobalMetaStateWasCalled; - int32_t mGeneration; - - public: - FakeInputReaderContext(InputReader* reader) - : ContextImpl(reader), - mGlobalMetaState(0), - mUpdateGlobalMetaStateWasCalled(false), - mGeneration(1) {} - - virtual ~FakeInputReaderContext() {} - - void assertUpdateGlobalMetaStateWasCalled() { - ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) - << "Expected updateGlobalMetaState() to have been called."; - mUpdateGlobalMetaStateWasCalled = false; - } - - void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; } - - uint32_t getGeneration() { return mGeneration; } - - void updateGlobalMetaState() override { - mUpdateGlobalMetaStateWasCalled = true; - ContextImpl::updateGlobalMetaState(); - } - - int32_t getGlobalMetaState() override { - return mGlobalMetaState | ContextImpl::getGlobalMetaState(); - } - - int32_t bumpGeneration() override { - mGeneration = ContextImpl::bumpGeneration(); - return mGeneration; - } - } mFakeContext; - - friend class InputReaderTest; - -public: - FakeInputReaderContext* getContext() { return &mFakeContext; } -}; - // --- InputReaderPolicyTest --- class InputReaderPolicyTest : public testing::Test { protected: sp<FakeInputReaderPolicy> mFakePolicy; - void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } + void SetUp() override { mFakePolicy = sp<FakeInputReaderPolicy>::make(); } void TearDown() override { mFakePolicy.clear(); } }; @@ -1377,9 +369,8 @@ TEST_F(InputReaderPolicyTest, Viewports_GetCleared) { ASSERT_FALSE(internalViewport); // Add an internal viewport, then clear it - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT, - ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, uniqueId, NO_PORT, ViewportType::INTERNAL); // Check matching by uniqueId internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId); @@ -1408,21 +399,21 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByType) { constexpr int32_t virtualDisplayId2 = 3; // Add an internal viewport - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId, - NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, internalUniqueId, NO_PORT, + ViewportType::INTERNAL); // Add an external viewport - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId, - NO_PORT, ViewportType::EXTERNAL); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, externalUniqueId, NO_PORT, + ViewportType::EXTERNAL); // Add an virtual viewport mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1, - NO_PORT, ViewportType::VIRTUAL); + ui::ROTATION_0, true /*isActive*/, virtualUniqueId1, NO_PORT, + ViewportType::VIRTUAL); // Add another virtual viewport mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2, - NO_PORT, ViewportType::VIRTUAL); + ui::ROTATION_0, true /*isActive*/, virtualUniqueId2, NO_PORT, + ViewportType::VIRTUAL); // Check matching by type for internal std::optional<DisplayViewport> internalViewport = @@ -1470,13 +461,11 @@ TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) { for (const ViewportType& type : types) { mFakePolicy->clearViewports(); // Add a viewport - mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, - NO_PORT, type); + mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, uniqueId1, NO_PORT, type); // Add another viewport - mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, - NO_PORT, type); + mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, uniqueId2, NO_PORT, type); // Check that correct display viewport was returned by comparing the display IDs. std::optional<DisplayViewport> viewport1 = @@ -1516,10 +505,10 @@ TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) { // Add the default display first and ensure it gets returned. mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT, + ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT, ViewportType::INTERNAL); mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT, + ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT, ViewportType::INTERNAL); std::optional<DisplayViewport> viewport = @@ -1531,10 +520,10 @@ TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) { // Add the default display second to make sure order doesn't matter. mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT, + ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT, ViewportType::INTERNAL); mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT, + ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT, ViewportType::INTERNAL); viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); @@ -1558,13 +547,11 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByPort) { mFakePolicy->clearViewports(); // Add a viewport that's associated with some display port that's not of interest. - mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3, - type); + mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, uniqueId1, hdmi3, type); // Add another viewport, connected to HDMI1 port - mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1, - type); + mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, uniqueId2, hdmi1, type); // Check that correct display viewport was returned by comparing the display ports. std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1); @@ -1595,7 +582,7 @@ protected: void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); - mFakePolicy = new FakeInputReaderPolicy(); + mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, @@ -1884,34 +871,37 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { mapper.addSupportedKeyCode(AKEYCODE_A); mapper.addSupportedKeyCode(AKEYCODE_B); - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; + const std::vector<int32_t> keyCodes{AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2}; uint8_t flags[4] = { 0, 0, 0, 1 }; - ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, 4, keyCodes, flags)) + ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, keyCodes, flags)) << "Should return false when device id is >= 0 but unknown."; ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) + ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, keyCodes, flags)) << "Should return false when device id is valid but the sources are not supported by " "the device."; ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, + ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, keyCodes, flags)) << "Should return value provided by mapper when device id is valid and the device " "supports some of the sources."; ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when the device id is < 0 but the sources are not supported by any device."; + ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, keyCodes, flags)) + << "Should return false when the device id is < 0 but the sources are not supported by " + "any device."; ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; + ASSERT_TRUE( + mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, keyCodes, flags)) + << "Should return value provided by mapper when device id is < 0 and one of the " + "devices supports some of the sources."; ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); } @@ -2014,11 +1004,10 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Add default and second display. mFakePolicy->clearViewports(); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT, - ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, "local:0", NO_PORT, ViewportType::INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1, + ui::ROTATION_0, true /*isActive*/, "local:1", hdmi1, ViewportType::EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); @@ -2217,8 +1206,9 @@ TEST_F(InputReaderTest, BatteryGetCapacity) { ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); - ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY); - ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY); + ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY), + FakeEventHub::BATTERY_CAPACITY); + ASSERT_EQ(mReader->getBatteryCapacity(deviceId), FakeEventHub::BATTERY_CAPACITY); } TEST_F(InputReaderTest, BatteryGetStatus) { @@ -2234,8 +1224,24 @@ TEST_F(InputReaderTest, BatteryGetStatus) { ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); - ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS); - ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS); + ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY), + FakeEventHub::BATTERY_STATUS); + ASSERT_EQ(mReader->getBatteryStatus(deviceId), FakeEventHub::BATTERY_STATUS); +} + +TEST_F(InputReaderTest, BatteryGetDevicePath) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + ftl::Flags<InputDeviceClass> deviceClass = + InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY; + constexpr int32_t eventHubId = 1; + const char* DEVICE_LOCATION = "BLUETOOTH"; + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); + device->addController<FakePeripheralController>(eventHubId); + mReader->pushNextDevice(device); + + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); + + ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), FakeEventHub::BATTERY_DEVPATH); } TEST_F(InputReaderTest, LightGetColor) { @@ -2279,7 +1285,10 @@ protected: std::shared_ptr<FakePointerController> mFakePointerController; void SetUp() override { - mFakePolicy = new FakeInputReaderPolicy(); +#if !defined(__ANDROID__) + GTEST_SKIP(); +#endif + mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakePointerController = std::make_shared<FakePointerController>(); mFakePolicy->setPointerController(mFakePointerController); mTestListener = std::make_unique<TestInputListener>(2000ms /*eventHappenedTimeout*/, @@ -2297,18 +1306,30 @@ protected: } void TearDown() override { +#if !defined(__ANDROID__) + return; +#endif ASSERT_EQ(mReader->stop(), OK); mReader.reset(); mTestListener.reset(); mFakePolicy.clear(); } + + std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) { + const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices(); + const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(), + [&name](const InputDeviceInfo& info) { + return info.getIdentifier().name == name; + }); + return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt; + } }; TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { // An invalid input device that is only used for this test. class InvalidUinputDevice : public UinputDevice { public: - InvalidUinputDevice() : UinputDevice("Invalid Device") {} + InvalidUinputDevice() : UinputDevice("Invalid Device", 99 /*productId*/) {} private: void configureDevice(int fd, uinput_user_dev* device) override {} @@ -2337,18 +1358,11 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); - // Find the test device by its name. - const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices(); - const auto& it = - std::find_if(inputDevices.begin(), inputDevices.end(), - [&keyboard](const InputDeviceInfo& info) { - return info.getIdentifier().name == keyboard->getName(); - }); - - ASSERT_NE(it, inputDevices.end()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources()); - ASSERT_EQ(0U, it->getMotionRanges().size()); + const auto device = findDeviceByName(keyboard->getName()); + ASSERT_TRUE(device.has_value()); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources()); + ASSERT_EQ(0U, device->getMotionRanges().size()); keyboard.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); @@ -2383,6 +1397,41 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { ASSERT_LE(keyArgs.eventTime, keyArgs.readTime); } +TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) { + std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + + const auto device = findDeviceByName(stylus->getName()); + ASSERT_TRUE(device.has_value()); + + // An external stylus with buttons should also be recognized as a keyboard. + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, device->getSources()) + << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str(); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType()); + + const auto DOWN = + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD)); + const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD)); + + stylus->pressAndReleaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + + stylus->pressAndReleaseKey(BTN_STYLUS2); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY)))); + + stylus->pressAndReleaseKey(BTN_STYLUS3); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY)))); +} + /** * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP @@ -2405,25 +1454,31 @@ TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) { ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode); } -// --- TouchProcessTest --- +// --- TouchIntegrationTest --- + class TouchIntegrationTest : public InputReaderIntegrationTest { protected: const std::string UNIQUE_ID = "local:0"; void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP(); +#endif InputReaderIntegrationTest::SetUp(); // At least add an internal display. - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT, - ViewportType::INTERNAL); + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto info = findDeviceByName(mDevice->getName()); + ASSERT_TRUE(info); + mDeviceInfo = *info; } void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, const std::string& uniqueId, + ui::Rotation orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort, ViewportType viewportType) { mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/, @@ -2443,8 +1498,17 @@ protected: } std::unique_ptr<UinputTouchScreen> mDevice; + InputDeviceInfo mDeviceInfo; }; +TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) { + // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus + // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button + // presses). + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, + mDeviceInfo.getSources()); +} + TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -2659,6 +1723,466 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); } +TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Send down with the pen tool selected. The policy should be notified of the stylus presence. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId())); + + // Release the stylus touch. + mDevice->sendUp(); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified()); + + // Touch down with the finger, without the pen tool selected. The policy is not notified. + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified()); + + mDevice->sendUp(); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); + + // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter. + // The policy should be notified of the stylus presence. + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendMove(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId())); +} + +// --- StylusButtonIntegrationTest --- + +// Verify the behavior of button presses reported by various kinds of styluses, including buttons +// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external +// stylus. +template <typename UinputStylusDevice> +class StylusButtonIntegrationTest : public TouchIntegrationTest { +protected: + void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP(); +#endif + TouchIntegrationTest::SetUp(); + mTouchscreen = mDevice.get(); + mTouchscreenInfo = mDeviceInfo; + + setUpStylusDevice(); + } + + UinputStylusDevice* mStylus{nullptr}; + InputDeviceInfo mStylusInfo{}; + + UinputTouchScreen* mTouchscreen{nullptr}; + InputDeviceInfo mTouchscreenInfo{}; + +private: + // When we are attempting to test stylus button events that are sent from the touchscreen, + // use the same Uinput device for the touchscreen and the stylus. + template <typename T = UinputStylusDevice> + std::enable_if_t<std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() { + mStylus = mDevice.get(); + mStylusInfo = mDeviceInfo; + } + + // When we are attempting to stylus buttons from an external stylus being merged with touches + // from a touchscreen, create a new Uinput device through which stylus buttons can be injected. + template <typename T = UinputStylusDevice> + std::enable_if_t<!std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() { + mStylusDeviceLifecycleTracker = createUinputDevice<T>(); + mStylus = mStylusDeviceLifecycleTracker.get(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto info = findDeviceByName(mStylus->getName()); + ASSERT_TRUE(info); + mStylusInfo = *info; + } + + std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{}; + + // Hide the base class's device to expose it with a different name for readability. + using TouchIntegrationTest::mDevice; + using TouchIntegrationTest::mDeviceInfo; +}; + +using StylusButtonIntegrationTestTypes = + ::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>; +TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes); + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) { + const auto stylusId = TestFixture::mStylusInfo.getId(); + + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); +} + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); + + // Press the stylus button. + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + + // Start and finish a stylus gesture. + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + // Release the stylus button. + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); +} + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); + auto toolTypeDevice = + AllOf(WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithDeviceId(touchscreenId)); + + // Press the stylus button. + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + + // Start hovering with the stylus. + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendMove(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Touch down with the stylus. + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Stop touching with the stylus, and start hovering. + TestFixture::mTouchscreen->sendUp(); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendMove(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Stop hovering. + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0)))); + // TODO(b/257971675): Fix inconsistent button state when exiting hover. + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Release the stylus button. + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); +} + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); + + // Start a stylus gesture. + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + // Press and release a stylus button. Each change in button state also generates a MOVE event. + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + // Finish the stylus gesture. + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); +} + +// --- ExternalStylusIntegrationTest --- + +// Verify the behavior of an external stylus. An external stylus can report pressure or button +// data independently of the touchscreen, which is then sent as a MotionEvent as part of an +// ongoing stylus gesture that is being emitted by the touchscreen. +using ExternalStylusIntegrationTest = TouchIntegrationTest; + +TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus capable of reporting pressure data that + // should be fused with a touch pointer. + std::unique_ptr<UinputExternalStylusWithPressure> stylus = + createUinputDevice<UinputExternalStylusWithPressure>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Set a pressure value on the stylus. It doesn't generate any events. + const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX; + stylus->setPressure(100); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Start a finger gesture, and ensure it shows up as stylus gesture + // with the pressure set by the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(100.f / RAW_PRESSURE_MAX)))); + + // Change the pressure on the external stylus, and ensure the touchscreen generates a MOVE + // event with the updated pressure. + stylus->setPressure(200); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX)))); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + +TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus capable of reporting pressure data that + // should be fused with a touch pointer. + std::unique_ptr<UinputExternalStylusWithPressure> stylus = + createUinputDevice<UinputExternalStylusWithPressure>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Set a pressure value of 0 on the stylus. It doesn't generate any events. + const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX; + // Send a non-zero value first to prevent the kernel from consuming the zero event. + stylus->setPressure(100); + stylus->setPressure(0); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Start a finger gesture. The touch device will withhold generating any touches for + // up to 72 milliseconds while waiting for pressure data from the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + auto waitUntil = std::chrono::system_clock::now() + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil)); + + // Since the external stylus did not report a pressure value within the timeout, + // it shows up as a finger pointer. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDeviceId(touchscreenId), + WithPressure(1.f)))); + + // Change the pressure on the external stylus. Since the pressure was not present at the start + // of the gesture, it is ignored for now. + stylus->setPressure(200); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Finish the finger gesture. + mDevice->sendTrackingId(INVALID_TRACKING_ID); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + // Start a new gesture. Since we have a valid pressure value, it shows up as a stylus. + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX)))); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + +TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus device that does not support pressure. It should not affect any + // touch pointers. + std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for + // pressure data from the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + auto waitUntil = std::chrono::system_clock::now() + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener + ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType( + AMOTION_EVENT_TOOL_TYPE_FINGER), + WithButtonState(0), + WithDeviceId(touchscreenId), + WithPressure(1.f)), + waitUntil)); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + // --- InputDeviceTest --- class InputDeviceTest : public testing::Test { protected: @@ -2669,6 +2193,7 @@ protected: static const int32_t DEVICE_CONTROLLER_NUMBER; static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES; static const int32_t EVENTHUB_ID; + static const std::string DEVICE_BLUETOOTH_ADDRESS; std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; @@ -2678,13 +2203,14 @@ protected: void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); - mFakePolicy = new FakeInputReaderPolicy(); + mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; + identifier.bluetoothAddress = DEVICE_BLUETOOTH_ADDRESS; mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION, identifier); mReader->pushNextDevice(mDevice); @@ -2706,6 +2232,7 @@ const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; const ftl::Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES = InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK; const int32_t InputDeviceTest::EVENTHUB_ID = 1; +const std::string InputDeviceTest::DEVICE_BLUETOOTH_ADDRESS = "11:AA:22:BB:33:CC"; TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(DEVICE_ID, mDevice->getId()); @@ -2713,6 +2240,17 @@ TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(ftl::Flags<InputDeviceClass>(0), mDevice->getClasses()); } +TEST_F(InputDeviceTest, CountryCodeCorrectlyMapped) { + mFakeEventHub->setCountryCode(EVENTHUB_ID, InputDeviceCountryCode::INTERNATIONAL); + + // Configuration + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); + InputReaderConfiguration config; + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0); + + ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode()); +} + TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { ASSERT_EQ(mDevice->isEnabled(), false); } @@ -2720,10 +2258,10 @@ TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { // Configuration. InputReaderConfiguration config; - mDevice->configure(ARBITRARY_TIME, &config, 0); + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0); // Reset. - mDevice->reset(ARBITRARY_TIME); + unused += mDevice->reset(ARBITRARY_TIME); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -2750,9 +2288,9 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 0)) << "Ignored device should return unknown switch state."; - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; + const std::vector<int32_t> keyCodes{AKEYCODE_A, AKEYCODE_B}; uint8_t flags[2] = { 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 2, keyCodes, flags)) + ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, keyCodes, flags)) << "Ignored device should never mark any key codes."; ASSERT_EQ(0, flags[0]) << "Flag for unsupported key should be unchanged."; ASSERT_EQ(1, flags[1]) << "Flag for unsupported key should be unchanged."; @@ -2760,7 +2298,7 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { // Configuration. - mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value")); + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "key", "value"); FakeInputMapper& mapper1 = mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); @@ -2779,18 +2317,18 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe mapper2.setMetaState(AMETA_SHIFT_ON); InputReaderConfiguration config; - mDevice->configure(ARBITRARY_TIME, &config, 0); + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0); - String8 propertyValue; - ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue)) + std::string propertyValue; + ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue)) << "Device should have read configuration during configuration phase."; - ASSERT_STREQ("value", propertyValue.string()); + ASSERT_EQ("value", propertyValue); ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled()); // Reset - mDevice->reset(ARBITRARY_TIME); + unused += mDevice->reset(ARBITRARY_TIME); ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled()); @@ -2827,16 +2365,16 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 4)) << "Should query mapper when source is supported."; - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; + const std::vector<int32_t> keyCodes{AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2}; uint8_t flags[4] = { 0, 0, 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) + ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, keyCodes, flags)) << "Should do nothing when source is unsupported."; ASSERT_EQ(0, flags[0]) << "Flag should be unchanged when source is unsupported."; ASSERT_EQ(0, flags[1]) << "Flag should be unchanged when source is unsupported."; ASSERT_EQ(0, flags[2]) << "Flag should be unchanged when source is unsupported."; ASSERT_EQ(1, flags[3]) << "Flag should be unchanged when source is unsupported."; - ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 4, keyCodes, flags)) + ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, keyCodes, flags)) << "Should query mapper when source is supported."; ASSERT_EQ(1, flags[0]) << "Flag for supported key should be set."; ASSERT_EQ(1, flags[1]) << "Flag for supported key should be set."; @@ -2846,7 +2384,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // Event handling. RawEvent event; event.deviceId = EVENTHUB_ID; - mDevice->process(&event, 1); + unused += mDevice->process(&event, 1); ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); @@ -2859,7 +2397,8 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN); // First Configuration. - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); // Device should be enabled by default. ASSERT_TRUE(mDevice->isEnabled()); @@ -2869,29 +2408,29 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { const std::string UNIQUE_ID = "local:1"; mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); // Device should be disabled because it is associated with a specific display via // input port <-> display port association, but the corresponding display is not found ASSERT_FALSE(mDevice->isEnabled()); // Prepare displays. mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi, + ui::ROTATION_0, true /*isActive*/, UNIQUE_ID, hdmi, ViewportType::INTERNAL); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_TRUE(mDevice->isEnabled()); // Device should be disabled after set disable. mFakePolicy->addDisabledDevice(mDevice->getId()); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_ENABLED_STATE); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_ENABLED_STATE); ASSERT_FALSE(mDevice->isEnabled()); // Device should still be disabled even found the associated display. - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_FALSE(mDevice->isEnabled()); } @@ -2899,47 +2438,49 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) { // Device should be enabled by default. mFakePolicy->clearViewports(); mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); ASSERT_TRUE(mDevice->isEnabled()); // Device should be disabled because it is associated with a specific display, but the // corresponding display is not found. mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_FALSE(mDevice->isEnabled()); // Device should be enabled when a display is found. mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, + ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_TRUE(mDevice->isEnabled()); // Device should be disabled after set disable. mFakePolicy->addDisabledDevice(mDevice->getId()); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_ENABLED_STATE); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_ENABLED_STATE); ASSERT_FALSE(mDevice->isEnabled()); // Device should still be disabled even found the associated display. - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_FALSE(mDevice->isEnabled()); } TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { mFakePolicy->clearViewports(); mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, + ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId()); } @@ -2958,161 +2499,11 @@ TEST_F(InputDeviceTest, DumpDoesNotCrash) { device.dump(dumpStr, eventHubDevStr); } -// --- InputMapperTest --- - -class InputMapperTest : public testing::Test { -protected: - static const char* DEVICE_NAME; - static const char* DEVICE_LOCATION; - static const int32_t DEVICE_ID; - static const int32_t DEVICE_GENERATION; - static const int32_t DEVICE_CONTROLLER_NUMBER; - static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES; - static const int32_t EVENTHUB_ID; - - std::shared_ptr<FakeEventHub> mFakeEventHub; - sp<FakeInputReaderPolicy> mFakePolicy; - std::unique_ptr<TestInputListener> mFakeListener; - std::unique_ptr<InstrumentedInputReader> mReader; - std::shared_ptr<InputDevice> mDevice; - - virtual void SetUp(ftl::Flags<InputDeviceClass> classes) { - mFakeEventHub = std::make_unique<FakeEventHub>(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = std::make_unique<TestInputListener>(); - mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - *mFakeListener); - mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); - // Consume the device reset notification generated when adding a new device. - mFakeListener->assertNotifyDeviceResetWasCalled(); - } - - void SetUp() override { - SetUp(DEVICE_CLASSES); - } - - void TearDown() override { - mFakeListener.reset(); - mFakePolicy.clear(); - } - - void addConfigurationProperty(const char* key, const char* value) { - mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value)); - } - - void configureDevice(uint32_t changes) { - if (!changes || - (changes & - (InputReaderConfiguration::CHANGE_DISPLAY_INFO | - InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) { - mReader->requestRefreshConfiguration(changes); - mReader->loopOnce(); - } - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); - // Loop the reader to flush the input listener queue. - mReader->loopOnce(); - } - - std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, - const std::string& location, int32_t eventHubId, - ftl::Flags<InputDeviceClass> classes) { - InputDeviceIdentifier identifier; - identifier.name = name; - identifier.location = location; - std::shared_ptr<InputDevice> device = - std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION, - identifier); - mReader->pushNextDevice(device); - mFakeEventHub->addDevice(eventHubId, name, classes); - mReader->loopOnce(); - return device; - } - - template <class T, typename... Args> - T& addMapperAndConfigure(Args... args) { - T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...); - configureDevice(0); - mDevice->reset(ARBITRARY_TIME); - mapper.reset(ARBITRARY_TIME); - // Loop the reader to flush the input listener queue. - mReader->loopOnce(); - return mapper; - } - - void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType viewportType) { - mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/, - uniqueId, physicalPort, viewportType); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - } - - void clearViewports() { - mFakePolicy->clearViewports(); - } - - void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code, - int32_t value) { - RawEvent event; - event.when = when; - event.readTime = readTime; - event.deviceId = mapper.getDeviceContext().getEventHubId(); - event.type = type; - event.code = code; - event.value = value; - mapper.process(&event); - // Loop the reader to flush the input listener queue. - mReader->loopOnce(); - } - - static void assertMotionRange(const InputDeviceInfo& info, - int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) { - const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); - ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source; - ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source; - ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source; - ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source; - ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source; - ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source; - ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source; - } - - static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, - float size, float touchMajor, float touchMinor, float toolMajor, - float toolMinor, float orientation, float distance, - float scaledAxisEpsilon = 1.f) { - ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon); - ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon); - ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); - ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); - ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - scaledAxisEpsilon); - ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - scaledAxisEpsilon); - ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - scaledAxisEpsilon); - ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - scaledAxisEpsilon); - ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); - ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON); - } - - static void assertPosition(const FakePointerController& controller, float x, float y) { - float actualX, actualY; - controller.getPosition(&actualX, &actualY); - ASSERT_NEAR(x, actualX, 1); - ASSERT_NEAR(y, actualY, 1); - } -}; - -const char* InputMapperTest::DEVICE_NAME = "device"; -const char* InputMapperTest::DEVICE_LOCATION = "USB1"; -const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000; -const int32_t InputMapperTest::DEVICE_GENERATION = 2; -const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0; -const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES = - ftl::Flags<InputDeviceClass>(0); // not needed for current tests -const int32_t InputMapperTest::EVENTHUB_ID = 1; +TEST_F(InputDeviceTest, GetBluetoothAddress) { + const auto& address = mReader->getBluetoothAddress(DEVICE_ID); + ASSERT_TRUE(address); + ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address); +} // --- SwitchInputMapperTest --- @@ -3138,14 +2529,17 @@ TEST_F(SwitchInputMapperTest, GetSwitchState) { TEST_F(SwitchInputMapperTest, Process) { SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>(); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifySwitchArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args)); + std::list<NotifyArgs> out; + out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1); + ASSERT_TRUE(out.empty()); + out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1); + ASSERT_TRUE(out.empty()); + out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0); + ASSERT_TRUE(out.empty()); + out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(1u, out.size()); + const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin()); ASSERT_EQ(ARBITRARY_TIME, args.eventTime); ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues); ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT), @@ -3192,22 +2586,23 @@ TEST_F(VibratorInputMapperTest, Vibrate) { ASSERT_FALSE(mapper.isVibrating()); // Start vibrating - mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN); + std::list<NotifyArgs> out = mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN); ASSERT_TRUE(mapper.isVibrating()); // Verify vibrator state listener was notified. mReader->loopOnce(); - NotifyVibratorStateArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_TRUE(args.isOn); + ASSERT_EQ(1u, out.size()); + const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); + ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId); + ASSERT_TRUE(vibrateArgs.isOn); // Stop vibrating - mapper.cancelVibrate(VIBRATION_TOKEN); + out = mapper.cancelVibrate(VIBRATION_TOKEN); ASSERT_FALSE(mapper.isVibrating()); // Verify vibrator state listener was notified. mReader->loopOnce(); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_FALSE(args.isOn); + ASSERT_EQ(1u, out.size()); + const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); + ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId); + ASSERT_FALSE(cancelArgs.isOn); } // --- SensorInputMapperTest --- @@ -3369,7 +2764,7 @@ class KeyboardInputMapperTest : public InputMapperTest { protected: const std::string UNIQUE_ID = "local:0"; - void prepareDisplay(int32_t orientation); + void prepareDisplay(ui::Rotation orientation); void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode, @@ -3379,7 +2774,7 @@ protected: /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the * orientation. */ -void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { +void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) { setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); } @@ -3517,6 +2912,27 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { ASSERT_EQ(ARBITRARY_TIME, args.downTime); } +TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0); + mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B); + + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + + // Key down by scan code. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEYCODE_B, args.keyCode); + + // Key up by scan code. + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEYCODE_B, args.keyCode); +} + /** * Ensure that the readTime is set to the time when the EV_KEY is received. */ @@ -3591,7 +3007,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -3613,7 +3029,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); ASSERT_NO_FATAL_FAILURE( testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, @@ -3624,7 +3040,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { AKEYCODE_DPAD_LEFT, DISPLAY_ID)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); ASSERT_NO_FATAL_FAILURE( testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, @@ -3635,7 +3051,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { AKEYCODE_DPAD_DOWN, DISPLAY_ID)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_180); + prepareDisplay(ui::ROTATION_180); ASSERT_NO_FATAL_FAILURE( testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, @@ -3646,7 +3062,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); + prepareDisplay(ui::ROTATION_270); ASSERT_NO_FATAL_FAILURE( testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, @@ -3660,7 +3076,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { // in the key up as we did in the key down. NotifyKeyArgs args; clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); + prepareDisplay(ui::ROTATION_270); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); @@ -3668,7 +3084,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_180); + prepareDisplay(ui::ROTATION_180); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); @@ -3693,7 +3109,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); @@ -3715,7 +3131,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { // Display id should be ADISPLAY_ID_NONE without any display configuration. // ^--- already checked by the previous test - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); @@ -3725,7 +3141,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { constexpr int32_t newDisplayId = 2; clearViewports(); - setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); @@ -3778,9 +3194,8 @@ TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags)); + ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, {AKEYCODE_A, AKEYCODE_B}, flags)); ASSERT_TRUE(flags[0]); ASSERT_FALSE(flags[1]); } @@ -3864,7 +3279,7 @@ TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) { AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); // Meta state should be AMETA_NONE after reset - mapper.reset(ARBITRARY_TIME); + std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME); ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); // Meta state should be AMETA_NONE with update, as device doesn't have the keys. mapper.updateMetaState(AKEYCODE_NUM_LOCK); @@ -3916,8 +3331,10 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { KeyboardInputMapper& mapper2 = device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); - device2->reset(ARBITRARY_TIME); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + 0 /*changes*/); + unused += device2->reset(ARBITRARY_TIME); // Prepared displays and associated info. constexpr uint8_t hdmi1 = 0; @@ -3928,19 +3345,19 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { mFakePolicy->addInputPortAssociation(USB2, hdmi2); // No associated display viewport found, should disable the device. - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_FALSE(device2->isEnabled()); // Prepare second display. constexpr int32_t newDisplayId = 2; - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, UNIQUE_ID, hdmi1, ViewportType::INTERNAL); - setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL); // Default device will reconfigure above, need additional reconfiguration for another device. - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); // Device should be enabled after the associated display is found. ASSERT_TRUE(mDevice->isEnabled()); @@ -4024,8 +3441,10 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { KeyboardInputMapper& mapper2 = device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); - device2->reset(ARBITRARY_TIME); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + 0 /*changes*/); + unused += device2->reset(ARBITRARY_TIME); ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL)); ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML)); @@ -4083,8 +3502,10 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { KeyboardInputMapper& mapper2 = device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); - device2->reset(ARBITRARY_TIME); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + 0 /*changes*/); + unused += device2->reset(ARBITRARY_TIME); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); @@ -4130,6 +3551,37 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); } +TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) { + const int32_t USAGE_A = 0x070004; + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // Key down by scan code. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + + // Disable device, it should synthesize cancellation events for down events. + mFakePolicy->addDisabledDevice(DEVICE_ID); + configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags); +} + // --- KeyboardInputMapperTest_ExternalDevice --- class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { @@ -4232,14 +3684,14 @@ protected: void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY); - void prepareDisplay(int32_t orientation) { + void prepareDisplay(ui::Rotation orientation) { setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); } void prepareSecondaryDisplay() { setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT, + ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::EXTERNAL); } @@ -4524,7 +3976,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotion addConfigurationProperty("cursor.orientationAware", "1"); CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); @@ -4543,7 +3995,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); @@ -4554,7 +4006,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); @@ -4565,7 +4017,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_180); + prepareDisplay(ui::ROTATION_180); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); @@ -4576,7 +4028,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); + prepareDisplay(ui::ROTATION_270); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); @@ -5040,7 +4492,7 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); // Ensure the display is rotated. - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); NotifyMotionArgs args; @@ -5076,7 +4528,7 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) { CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); // Set up the default display. - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); // Set up the secondary display as the display on which the pointer should be shown. // The InputDevice is not associated with any display. @@ -5093,8 +4545,9 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), - WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(110.0f, 220.0f)))); ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } @@ -5102,7 +4555,7 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) { CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); // Set up the default display. - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); // Set up the secondary display as the display on which the pointer should be shown, // and associate the InputDevice with the secondary display. @@ -5119,8 +4572,9 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), - WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(110.0f, 220.0f)))); ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } @@ -5128,7 +4582,7 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); // Set up the default display as the display on which the pointer should be shown. - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); // Associate the InputDevice with the secondary display. @@ -5144,6 +4598,106 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +// --- BluetoothCursorInputMapperTest --- + +class BluetoothCursorInputMapperTest : public CursorInputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); +} + // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { @@ -5195,9 +4749,9 @@ protected: TOOL_TYPE = 1 << 10, }; - void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT); + void prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port = NO_PORT); void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT); - void prepareVirtualDisplay(int32_t orientation); + void prepareVirtualDisplay(ui::Rotation orientation); void prepareVirtualKeys(); void prepareLocationCalibration(); int32_t toRawX(float displayX); @@ -5251,17 +4805,17 @@ const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 }, }; -void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) { +void TouchInputMapperTest::prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port) { setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID, port, ViewportType::INTERNAL); } void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) { setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type); + ui::ROTATION_0, SECONDARY_UNIQUE_ID, port, type); } -void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) { +void TouchInputMapperTest::prepareVirtualDisplay(ui::Rotation orientation) { setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIRTUAL); @@ -5414,26 +4968,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNot prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { - mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X); - mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y); - prepareButtons(); - prepareAxes(POSITION); - SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) { - prepareButtons(); - prepareAxes(POSITION); - addConfigurationProperty("touch.deviceType", "touchPad"); - SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) { @@ -5447,7 +4982,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_Return TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5475,7 +5010,7 @@ TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5503,22 +5038,22 @@ TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A }; uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags)); + ASSERT_TRUE( + mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, {AKEYCODE_HOME, AKEYCODE_A}, flags)); ASSERT_TRUE(flags[0]); ASSERT_FALSE(flags[1]); } TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5568,7 +5103,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5689,7 +5224,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5764,7 +5299,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDispl addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID); - prepareVirtualDisplay(DISPLAY_ORIENTATION_0); + prepareVirtualDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5860,7 +5395,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDispl TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); @@ -5959,7 +5494,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_DoesNotRotateMot NotifyMotionArgs args; // Rotation 90. - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); processDown(mapper, toRawX(50), toRawY(75)); processSync(mapper); @@ -5985,7 +5520,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotion // Rotation 0. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); processDown(mapper, toRawX(50), toRawY(75)); processSync(mapper); @@ -5999,7 +5534,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotion // Rotation 90. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN); processSync(mapper); @@ -6013,7 +5548,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotion // Rotation 180. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_180); + prepareDisplay(ui::ROTATION_180); processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN); processSync(mapper); @@ -6027,7 +5562,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotion // Rotation 270. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); + prepareDisplay(ui::ROTATION_270); processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50)); processSync(mapper); @@ -6047,7 +5582,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) { addConfigurationProperty("touch.orientationAware", "1"); addConfigurationProperty("touch.orientation", "ORIENTATION_0"); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -6071,7 +5606,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) { addConfigurationProperty("touch.orientationAware", "1"); addConfigurationProperty("touch.orientation", "ORIENTATION_90"); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -6095,7 +5630,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) { addConfigurationProperty("touch.orientationAware", "1"); addConfigurationProperty("touch.orientation", "ORIENTATION_180"); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -6119,7 +5654,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) { addConfigurationProperty("touch.orientationAware", "1"); addConfigurationProperty("touch.orientation", "ORIENTATION_270"); clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs args; @@ -6150,7 +5685,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio // Orientation 90, Rotation 0. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50)); processSync(mapper); @@ -6164,7 +5699,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio // Orientation 90, Rotation 90. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); processDown(mapper, toRotatedRawX(50), toRotatedRawY(75)); processSync(mapper); @@ -6178,7 +5713,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio // Orientation 90, Rotation 180. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_180); + prepareDisplay(ui::ROTATION_180); processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN); processSync(mapper); @@ -6192,7 +5727,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio // Orientation 90, Rotation 270. clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); + prepareDisplay(ui::ROTATION_270); processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN); processSync(mapper); @@ -6208,7 +5743,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); @@ -6252,7 +5787,7 @@ TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareLocationCalibration(); prepareButtons(); prepareAxes(POSITION); @@ -6275,7 +5810,7 @@ TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); @@ -6518,7 +6053,7 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) { TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); @@ -6653,7 +6188,7 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); @@ -6725,7 +6260,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueI TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION | PRESSURE); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); @@ -6794,13 +6329,34 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } +TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareButtons(); + prepareAxes(POSITION | PRESSURE); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + // Touch down. + processDown(mapper, 100, 200); + processPressure(mapper, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + // Reset the mapper. This should cancel the ongoing gesture. + resetMapper(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION | PRESSURE); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - NotifyMotionArgs motionArgs; // Set the initial state for the touch pointer. mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100); @@ -6809,80 +6365,30 @@ TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) { mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1); // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch - // state by reading the current axis values. - mapper.reset(ARBITRARY_TIME); + // state by reading the current axis values. Since there was no ongoing gesture, calling reset + // does not generate any events. + resetMapper(mapper, ARBITRARY_TIME); // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use // the recreated touch state to generate a down event. processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) { - std::shared_ptr<FakePointerController> fakePointerController = - std::make_shared<FakePointerController>(); - fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - fakePointerController->setPosition(100, 200); - fakePointerController->setButtonState(0); - mFakePolicy->setPointerController(fakePointerController); - - addConfigurationProperty("touch.deviceType", "pointer"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareButtons(); - mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0); - prepareAxes(POSITION); - SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); - - // Start a stylus gesture. - processKey(mapper, BTN_TOOL_PEN, 1); - processDown(mapper, 100, 200); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_DOWN), - WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); - // TODO(b/257078296): Pointer mode generates extra event. ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_MOVE), - WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f)))); - // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus - // gesture should be disabled. - auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - viewport->isActive = false; - mFakePolicy->updateViewport(*viewport); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL), - WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); - // TODO(b/257078296): Pointer mode generates extra event. - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL), - WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } TEST_F(SingleTouchInputMapperTest, Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); NotifyMotionArgs motionArgs; // Down. - int32_t x = 100; - int32_t y = 200; - processDown(mapper, x, y); + processDown(mapper, 100, 200); processSync(mapper); // We should receive a down event @@ -6903,7 +6409,7 @@ TEST_F(SingleTouchInputMapperTest, TEST_F(SingleTouchInputMapperTest, Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); @@ -6953,14 +6459,52 @@ TEST_F(SingleTouchInputMapperTest, // In the next sync, the touch state that was recreated when the device was reset is reported. processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); // No more events. ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); } +TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Press a stylus button. + processKey(mapper, BTN_STYLUS, 1); + processSync(mapper); + + // Start a touch gesture and ensure the BUTTON_PRESS event is generated. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(toDisplayX(100), toDisplayY(200)), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(toDisplayX(100), toDisplayY(200)), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Release the touch gesture. Ensure that the BUTTON_RELEASE event is generated even though + // the button has not actually been released, since there will be no pointers through which the + // button state can be reported. The event is generated at the location of the pointer before + // it went up. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0)))); +} + // --- TouchDisplayProjectionTest --- class TouchDisplayProjectionTest : public SingleTouchInputMapperTest { @@ -6968,27 +6512,25 @@ public: // The values inside DisplayViewport are expected to be pre-rotated. This updates the current // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the // rotated equivalent of the given un-rotated physical display bounds. - void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) { + void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) { uint32_t inverseRotationFlags; auto width = DISPLAY_WIDTH; auto height = DISPLAY_HEIGHT; switch (orientation) { - case DISPLAY_ORIENTATION_90: + case ui::ROTATION_90: inverseRotationFlags = ui::Transform::ROT_270; std::swap(width, height); break; - case DISPLAY_ORIENTATION_180: + case ui::ROTATION_180: inverseRotationFlags = ui::Transform::ROT_180; break; - case DISPLAY_ORIENTATION_270: + case ui::ROTATION_270: inverseRotationFlags = ui::Transform::ROT_90; std::swap(width, height); break; - case DISPLAY_ORIENTATION_0: + case ui::ROTATION_0: inverseRotationFlags = ui::Transform::ROT_0; break; - default: - FAIL() << "Invalid orientation: " << orientation; } const ui::Transform rotation(inverseRotationFlags, width, height); @@ -7032,7 +6574,7 @@ public: TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); @@ -7047,8 +6589,7 @@ TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) { static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{ {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}}; - for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, - DISPLAY_ORIENTATION_270}) { + for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) { configurePhysicalDisplay(orientation, kPhysicalDisplay); // Touches outside the physical display should be ignored, and should not generate any @@ -7068,7 +6609,7 @@ TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) { TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); @@ -7081,8 +6622,7 @@ TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800. static const Rect kPhysicalDisplay{10, 20, 70, 160}; - for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, - DISPLAY_ORIENTATION_270}) { + for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) { configurePhysicalDisplay(orientation, kPhysicalDisplay); // Touches that start outside the physical display should be ignored until it enters the @@ -7127,6 +6667,360 @@ TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { } } +// --- ExternalStylusFusionTest --- + +class ExternalStylusFusionTest : public SingleTouchInputMapperTest { +public: + SingleTouchInputMapper& initializeInputMapperWithExternalStylus() { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareButtons(); + prepareAxes(POSITION); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + mStylusState.when = ARBITRARY_TIME; + mStylusState.pressure = 0.f; + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo}); + configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); + processExternalStylusState(mapper); + return mapper; + } + + std::list<NotifyArgs> processExternalStylusState(InputMapper& mapper) { + std::list<NotifyArgs> generatedArgs = mapper.updateExternalStylusState(mStylusState); + for (const NotifyArgs& args : generatedArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + return generatedArgs; + } + +protected: + StylusState mStylusState{}; + static constexpr uint32_t EXPECTED_SOURCE = + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS; + + void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) { + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + // The first pointer is withheld. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // The external stylus reports pressure. The withheld finger pointer is released as a + // stylus. + mStylusState.pressure = 1.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // Subsequent pointer events are not withheld. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + + void testSuccessfulFusionGesture(SingleTouchInputMapper& mapper) { + ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper)); + + // Releasing the touch pointer ends the gesture. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + mStylusState.pressure = 0.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + + void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) { + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)); + + // The first pointer is withheld when an external stylus is connected, + // and a timeout is requested. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // If the timeout expires early, it is requested again. + handleTimeout(mapper, ARBITRARY_TIME + 1); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // When the timeout expires, the withheld touch is released as a finger pointer. + handleTimeout(mapper, ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + + // Subsequent pointer events are not withheld. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + +private: + InputDeviceInfo mExternalStylusDeviceInfo{}; +}; + +TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources()); +} + +TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); +} + +TEST_F(ExternalStylusFusionTest, SuccessfulFusion_TouchFirst) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); +} + +// Test a successful stylus fusion gesture where the pressure is reported by the external +// before the touch is reported by the touchscreen. +TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + // The external stylus reports pressure first. It is ignored for now. + mStylusState.pressure = 1.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // When the touch goes down afterwards, it is reported as a stylus pointer. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusionIsRepeatedForEachNewGesture) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); + + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + mStylusState.pressure = 0.8f; + processExternalStylusState(mapper); + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithPressure(0.8f)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The external stylus reports a pressure change. We wait for some time for a touch event. + mStylusState.pressure = 0.6f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated pressure. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.6f)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // There is another pressure change. + mStylusState.pressure = 0.5f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new pressure. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + + // If a zero pressure is reported before the touch goes up, the previous pressure value is + // repeated indefinitely. + mStylusState.pressure = 0.0f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + processMove(mapper, 102, 202); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + processMove(mapper, 103, 203); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto source = WithSource(EXPECTED_SOURCE); + + mStylusState.pressure = 1.f; + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_ERASER; + processExternalStylusState(mapper); + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_ERASER)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The external stylus reports a tool change. We wait for some time for a touch event. + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated pressure. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // There is another tool type change. + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new tool type. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper)); + + // The external stylus reports a button change. We wait for some time for a touch event. + mStylusState.buttons = AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated button state. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The button is now released. + mStylusState.buttons = 0; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new button state. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(0)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + // --- MultiTouchInputMapperTest --- class MultiTouchInputMapperTest : public TouchInputMapperTest { @@ -7145,8 +7039,10 @@ protected: void processSlot(MultiTouchInputMapper& mapper, int32_t slot); void processToolType(MultiTouchInputMapper& mapper, int32_t toolType); void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value); + void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value); void processMTSync(MultiTouchInputMapper& mapper); - void processSync(MultiTouchInputMapper& mapper); + void processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime = ARBITRARY_TIME, + nsecs_t readTime = READ_TIME); }; void MultiTouchInputMapperTest::prepareAxes(int axes) { @@ -7249,17 +7145,24 @@ void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value); } +void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, + int32_t value) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value); +} + void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0); } -void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); +void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime, + nsecs_t readTime) { + process(mapper, eventTime, readTime, EV_SYN, SYN_REPORT, 0); } TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7531,7 +7434,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackin TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0, /*fuzz*/ 0, /*resolution*/ 10); @@ -7561,7 +7464,7 @@ TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) { TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0, /*fuzz*/ 0, /*resolution*/ 10); @@ -7582,7 +7485,7 @@ TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupport TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID); prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7753,7 +7656,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7919,7 +7822,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) { TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -7968,7 +7871,7 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL | MINOR); addConfigurationProperty("touch.size.calibration", "geometric"); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8005,7 +7908,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL); addConfigurationProperty("touch.size.calibration", "diameter"); addConfigurationProperty("touch.size.scale", "10"); @@ -8056,7 +7959,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibrati TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL); addConfigurationProperty("touch.size.calibration", "area"); addConfigurationProperty("touch.size.scale", "43"); @@ -8089,7 +7992,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) { TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | PRESSURE); addConfigurationProperty("touch.pressure.calibration", "amplitude"); addConfigurationProperty("touch.pressure.scale", "0.01"); @@ -8123,7 +8026,7 @@ TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) { TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8364,9 +8267,66 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(0, motionArgs.buttonState); } +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareAxes(POSITION | ID | SLOT); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0); + mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0); + + // Touch down. + processId(mapper, 1); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0)))); + + // Press and release button mapped to the primary stylus button. + processKey(mapper, BTN_A, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + processKey(mapper, BTN_A, 0); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + + // Press and release the HID usage mapped to the secondary stylus button. + processHidUsage(mapper, 0xabcd, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY)))); + + processHidUsage(mapper, 0xabcd, 0); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + + // Release touch. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0)))); +} + TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8516,7 +8476,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8587,7 +8547,7 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8687,7 +8647,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // Add viewport for display 1 on hdmi1 - prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1); + prepareDisplay(ui::ROTATION_0, hdmi1); // Send a touch event again processPosition(mapper, 100, 100); processSync(mapper); @@ -8704,8 +8664,8 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayUniqueId) { mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareVirtualDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); + prepareVirtualDisplay(ui::ROTATION_0); // Send a touch event processPosition(mapper, 100, 100); @@ -8728,12 +8688,12 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); prepareSecondaryDisplay(ViewportType::EXTERNAL); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - // Check source is mouse that would obtain the PointerController. - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + // Check source is a touchpad that would obtain the PointerController. + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); NotifyMotionArgs motionArgs; processPosition(mapper, 100, 100); @@ -8752,7 +8712,7 @@ TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) { prepareAxes(POSITION); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1); process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100); process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100); @@ -8776,9 +8736,9 @@ TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) { */ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { addConfigurationProperty("touch.deviceType", "touchScreen"); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT, - ViewportType::INTERNAL); + // Don't set touch.enableForInactiveViewport to verify the default behavior. + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); prepareAxes(POSITION); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -8790,11 +8750,32 @@ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { mFakeListener->assertNotifyMotionWasNotCalled(); } +/** + * When the viewport is not active (isActive=false) and touch.enableForInactiveViewport is true, + * the touch mapper can process the events and the events can be delivered to the listener. + */ +TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.enableForInactiveViewport", "1"); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + NotifyMotionArgs motionArgs; + processPosition(mapper, 100, 100); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); +} + TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { addConfigurationProperty("touch.deviceType", "touchScreen"); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT, - ViewportType::INTERNAL); + addConfigurationProperty("touch.enableForInactiveViewport", "0"); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); std::optional<DisplayViewport> optionalDisplayViewport = mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID); ASSERT_TRUE(optionalDisplayViewport.has_value()); @@ -8870,8 +8851,10 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Setup the second touch screen device. MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID); - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); - device2->reset(ARBITRARY_TIME); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + 0 /*changes*/); + unused += device2->reset(ARBITRARY_TIME); // Setup PointerController. std::shared_ptr<FakePointerController> fakePointerController = @@ -8886,13 +8869,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { mFakePolicy->setShowTouches(true); // Create displays. - prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1); + prepareDisplay(ui::ROTATION_0, hdmi1); prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2); // Default device will reconfigure above, need additional reconfiguration for another device. - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO | - InputReaderConfiguration::CHANGE_SHOW_TOUCHES); + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); // Two fingers down at default display. int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; @@ -8922,8 +8905,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Disable the show touches configuration and ensure the spots are cleared. mFakePolicy->setShowTouches(false); - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_SHOW_TOUCHES); + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); ASSERT_TRUE(fakePointerController->getSpots().empty()); } @@ -8931,7 +8914,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); NotifyMotionArgs motionArgs; @@ -8962,8 +8945,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreNotRotated) { NotifyMotionArgs motionArgs; // Test all 4 orientations - for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, - DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) { + for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) { SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation)); clearViewports(); prepareDisplay(orientation); @@ -8988,8 +8970,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated NotifyMotionArgs motionArgs; // Test all 4 orientations - for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, - DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) { + for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) { SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation)); clearViewports(); prepareDisplay(orientation); @@ -9023,7 +9004,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreNotRotated) { std::vector<TouchVideoFrame> frames{frame1, frame2, frame3}; NotifyMotionArgs motionArgs; - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); @@ -9046,7 +9027,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_MultipleFr std::vector<TouchVideoFrame> frames{frame1, frame2, frame3}; NotifyMotionArgs motionArgs; - prepareDisplay(DISPLAY_ORIENTATION_90); + prepareDisplay(ui::ROTATION_90); mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); @@ -9056,7 +9037,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_MultipleFr // compared to the display. This is so that when the window transform (which contains the // display rotation) is applied later by InputDispatcher, the coordinates end up in the // window's coordinate space. - frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90)); + frame.rotate(getInverseRotation(ui::ROTATION_90)); }); ASSERT_EQ(frames, motionArgs.videoFrames); } @@ -9093,7 +9074,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9138,7 +9119,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { */ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9186,7 +9167,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer */ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9261,7 +9242,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) */ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9359,7 +9340,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelW */ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9431,7 +9412,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPoin */ TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); @@ -9488,19 +9469,17 @@ TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) { TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - NotifyMotionArgs motionArgs; - // First finger down. processId(mapper, FIRST_TRACKING_ID); processPosition(mapper, 100, 200); processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); // Second finger down. processSlot(mapper, SECOND_SLOT); @@ -9509,50 +9488,47 @@ TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) { processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action); + mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN))); // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be - // preserved. Resetting should not generate any events. - mapper.reset(ARBITRARY_TIME); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + // preserved. Resetting should cancel the ongoing gesture. + resetMapper(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use // the existing touch state to generate a down event. processPosition(mapper, 301, 302); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f)))); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) { addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - NotifyMotionArgs motionArgs; - // First finger touches down and releases. processId(mapper, FIRST_TRACKING_ID); processPosition(mapper, 100, 200); processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); processId(mapper, INVALID_TRACKING_ID); processSync(mapper); ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); // Reset the mapper. When the mapper is reset, we expect it to restore the latest // raw state where no pointers are down. - mapper.reset(ARBITRARY_TIME); + resetMapper(mapper, ARBITRARY_TIME); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // Send an empty sync frame. Since there are no pointers, no events are generated. @@ -9560,6 +9536,54 @@ TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the + // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source. + // Due to limitations in the evdev protocol, we cannot say for certain that a device is capable + // of reporting stylus events just because it supports ABS_MT_TOOL_TYPE. + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); + + // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be + // reported with the stylus source. + processId(mapper, FIRST_TRACKING_ID); + processToolType(mapper, MT_TOOL_PEN); + processPosition(mapper, 100, 200); + processPressure(mapper, RAW_PRESSURE_MAX); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + // Now that we know the device supports styluses, ensure that the device is re-configured with + // the stylus source. + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources()); + { + const auto& devices = mReader->getInputDevices(); + auto deviceInfo = + std::find_if(devices.begin(), devices.end(), + [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; }); + LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice"); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources()); + } + + // Ensure the device was not reset to prevent interruptions of any ongoing gestures. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); + + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); +} + // --- MultiTouchInputMapperTest_ExternalDevice --- class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { @@ -9573,7 +9597,7 @@ protected: TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); @@ -9604,7 +9628,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { fakePointerController->setButtonState(0); // prepare device and capture - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); @@ -9739,11 +9763,11 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - // non captured touchpad should be a mouse source + // A non captured touchpad should have a mouse and touchpad source. mFakePolicy->setPointerCapture(false); configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) { @@ -9754,7 +9778,7 @@ TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) { fakePointerController->setButtonState(0); // prepare device and capture - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); @@ -9795,22 +9819,418 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { std::shared_ptr<FakePointerController> fakePointerController = std::make_shared<FakePointerController>(); - prepareDisplay(DISPLAY_ORIENTATION_0); + prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakePolicy->setPointerController(fakePointerController); mFakePolicy->setPointerCapture(false); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - // uncaptured touchpad should be a pointer device - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + // An uncaptured touchpad should be a pointer device, with additional touchpad source. + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); - // captured touchpad should be a touchpad device + // A captured touchpad should just have a touchpad source. mFakePolicy->setPointerCapture(true); configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } +// --- BluetoothMultiTouchInputMapperTest --- + +class BluetoothMultiTouchInputMapperTest : public MultiTouchInputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + } +}; + +TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareAxes(POSITION | ID | SLOT | PRESSURE); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + // Touch down. + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, 100, 200); + processPressure(mapper, RAW_PRESSURE_MAX); + processSync(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithEventTime(ARBITRARY_TIME)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + processPosition(mapper, 101 + i, 201 + i); + processSync(mapper, kernelEventTime); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithEventTime(expectedEventTime)))); + } + + // Release the touch. + processId(mapper, INVALID_TRACKING_ID); + processPressure(mapper, RAW_PRESSURE_MIN); + processSync(mapper, ARBITRARY_TIME + ms2ns(50)); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithEventTime(ARBITRARY_TIME + ms2ns(50))))); +} + +// --- MultiTouchPointerModeTest --- + +class MultiTouchPointerModeTest : public MultiTouchInputMapperTest { +protected: + float mPointerMovementScale; + float mPointerXZoomScale; + void preparePointerMode(int xAxisResolution, int yAxisResolution) { + addConfigurationProperty("touch.deviceType", "pointer"); + std::shared_ptr<FakePointerController> fakePointerController = + std::make_shared<FakePointerController>(); + fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + fakePointerController->setPosition(0, 0); + fakePointerController->setButtonState(0); + prepareDisplay(ui::ROTATION_0); + + prepareAxes(POSITION); + prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution); + // In order to enable swipe and freeform gesture in pointer mode, pointer capture + // needs to be disabled, and the pointer gesture needs to be enabled. + mFakePolicy->setPointerCapture(false); + mFakePolicy->setPointerGestureEnabled(true); + mFakePolicy->setPointerController(fakePointerController); + + float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN); + float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT); + mPointerMovementScale = + mFakePolicy->getPointerGestureMovementSpeedRatio() * displayDiagonal / rawDiagonal; + mPointerXZoomScale = + mFakePolicy->getPointerGestureZoomSpeedRatio() * displayDiagonal / rawDiagonal; + } + + void prepareAbsoluteAxisResolution(int xAxisResolution, int yAxisResolution) { + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, + /*flat*/ 0, + /*fuzz*/ 0, /*resolution*/ xAxisResolution); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, + /*flat*/ 0, + /*fuzz*/ 0, /*resolution*/ yAxisResolution); + } +}; + +/** + * Two fingers down on a pointer mode touch pad. The width + * of the two finger is larger than 1/4 of the touch pack diagnal length. However, it + * is smaller than the fixed min physical length 30mm. Two fingers' distance must + * be greater than the both value to be freeform gesture, so that after two + * fingers start to move downwards, the gesture should be swipe. + */ +TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) { + // The min freeform gesture width is 25units/mm x 30mm = 750 + // which is greater than fraction of the diagnal length of the touchpad (349). + // Thus, MaxSwipWidth is 750. + preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + NotifyMotionArgs motionArgs; + + // Two fingers down at once. + // The two fingers are 450 units apart, expects the current gesture to be PRESS + // Pointer's initial position is used the [0,0] coordinate. + int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + ASSERT_NO_FATAL_FAILURE( + assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)); + + // It should be recognized as a SWIPE gesture when two fingers start to move down, + // that there should be 1 pointer. + int32_t movingDistance = 200; + y1 += movingDistance; + y2 += movingDistance; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0, + movingDistance * mPointerMovementScale, 1, 0, 0, 0, + 0, 0, 0, 0)); +} + +/** + * Two fingers down on a pointer mode touch pad. The width of the two finger is larger + * than the minimum freeform gesture width, 30mm. However, it is smaller than 1/4 of + * the touch pack diagnal length. Two fingers' distance must be greater than the both + * value to be freeform gesture, so that after two fingers start to move downwards, + * the gesture should be swipe. + */ +TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) { + // The min freeform gesture width is 5units/mm x 30mm = 150 + // which is greater than fraction of the diagnal length of the touchpad (349). + // Thus, MaxSwipWidth is the fraction of the diagnal length, 349. + preparePointerMode(5 /*xResolution*/, 5 /*yResolution*/); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + NotifyMotionArgs motionArgs; + + // Two fingers down at once. + // The two fingers are 250 units apart, expects the current gesture to be PRESS + // Pointer's initial position is used the [0,0] coordinate. + int32_t x1 = 100, y1 = 125, x2 = 350, y2 = 125; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + ASSERT_NO_FATAL_FAILURE( + assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)); + + // It should be recognized as a SWIPE gesture when two fingers start to move down, + // and there should be 1 pointer. + int32_t movingDistance = 200; + y1 += movingDistance; + y2 += movingDistance; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification); + // New coordinate is the scaled relative coordinate from the initial coordinate. + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0, + movingDistance * mPointerMovementScale, 1, 0, 0, 0, + 0, 0, 0, 0)); +} + +/** + * Touch the touch pad with two fingers with a distance wider than the minimum freeform + * gesture width and 1/4 of the diagnal length of the touchpad. Expect to receive + * freeform gestures after two fingers start to move downwards. + */ +TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) { + preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + NotifyMotionArgs motionArgs; + + // Two fingers down at once. Wider than the max swipe width. + // The gesture is expected to be PRESS, then transformed to FREEFORM + int32_t x1 = 100, y1 = 125, x2 = 900, y2 = 125; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + // One pointer for PRESS, and its coordinate is used as the origin for pointer coordinates. + ASSERT_NO_FATAL_FAILURE( + assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)); + + int32_t movingDistance = 200; + + // Move two fingers down, expect a cancel event because gesture is changing to freeform, + // then two down events for two pointers. + y1 += movingDistance; + y2 += movingDistance; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + // The previous PRESS gesture is cancelled, because it is transformed to freeform + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + ASSERT_EQ(2U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + // Two pointers' scaled relative coordinates from their initial centroid. + // Initial y coordinates are 0 as y1 and y2 have the same value. + float cookedX1 = (x1 - x2) / 2 * mPointerXZoomScale; + float cookedX2 = (x2 - x1) / 2 * mPointerXZoomScale; + // When pointers move, the new coordinates equal to the initial coordinates plus + // scaled moving distance. + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1, + movingDistance * mPointerMovementScale, 1, 0, 0, 0, + 0, 0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], cookedX2, + movingDistance * mPointerMovementScale, 1, 0, 0, 0, + 0, 0, 0, 0)); + + // Move two fingers down again, expect one MOVE motion event. + y1 += movingDistance; + y2 += movingDistance; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(2U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1, + movingDistance * 2 * mPointerMovementScale, 1, 0, 0, + 0, 0, 0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], cookedX2, + movingDistance * 2 * mPointerMovementScale, 1, 0, 0, + 0, 0, 0, 0, 0)); +} + +TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) { + preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + NotifyMotionArgs motionArgs; + + // Place two fingers down. + int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(MotionClassification::NONE, motionArgs.classification); + ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET)); + ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET)); + + // Move the two fingers down and to the left. + int32_t movingDistance = 200; + x1 -= movingDistance; + y1 += movingDistance; + x2 -= movingDistance; + y2 += movingDistance; + + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processMTSync(mapper); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processMTSync(mapper); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(1U, motionArgs.pointerCount); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification); + ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0); + ASSERT_GT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET), 0); +} + +TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) { + preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Start a stylus gesture. + processKey(mapper, BTN_TOOL_PEN, 1); + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + // TODO(b/257078296): Pointer mode generates extra event. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus + // gesture should be disabled. + auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + viewport->isActive = false; + mFakePolicy->updateViewport(*viewport); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + // TODO(b/257078296): Pointer mode generates extra event. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + // --- JoystickInputMapperTest --- class JoystickInputMapperTest : public InputMapperTest { @@ -9836,7 +10256,7 @@ protected: process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); } - void prepareVirtualDisplay(int32_t orientation) { + void prepareVirtualDisplay(ui::Rotation orientation) { setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIRTUAL); @@ -9854,7 +10274,7 @@ TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) { mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID); - prepareVirtualDisplay(DISPLAY_ORIENTATION_0); + prepareVirtualDisplay(ui::ROTATION_0); // Send an axis event processAxis(mapper, ABS_X, 100); @@ -9892,7 +10312,7 @@ protected: virtual void SetUp(ftl::Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); - mFakePolicy = new FakeInputReaderPolicy(); + mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); @@ -9906,12 +10326,12 @@ protected: mFakePolicy.clear(); } - void configureDevice(uint32_t changes) { + std::list<NotifyArgs> configureDevice(uint32_t changes) { if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mReader->requestRefreshConfiguration(changes); mReader->loopOnce(); } - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + return mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); } std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, @@ -9957,15 +10377,17 @@ protected: TEST_F(BatteryControllerTest, GetBatteryCapacity) { PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); - ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY)); - ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY); + ASSERT_TRUE(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY)); + ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY).value_or(-1), + FakeEventHub::BATTERY_CAPACITY); } TEST_F(BatteryControllerTest, GetBatteryStatus) { PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); - ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY)); - ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS); + ASSERT_TRUE(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY)); + ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY).value_or(-1), + FakeEventHub::BATTERY_STATUS); } // --- LightControllerTest --- @@ -9978,7 +10400,7 @@ protected: TEST_F(LightControllerTest, MonoLight) { RawLightInfo infoMono = {.id = 1, - .name = "Mono", + .name = "mono_light", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS, .path = ""}; @@ -9989,7 +10411,29 @@ TEST_F(LightControllerTest, MonoLight) { controller.populateDeviceInfo(&info); std::vector<InputDeviceLightInfo> lights = info.getLights(); ASSERT_EQ(1U, lights.size()); - ASSERT_EQ(InputDeviceLightType::MONO, lights[0].type); + ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + + ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS)); + ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS); +} + +TEST_F(LightControllerTest, MonoKeyboardBacklight) { + RawLightInfo infoMono = {.id = 1, + .name = "mono_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono)); + + PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); + InputDeviceInfo info; + controller.populateDeviceInfo(&info); + std::vector<InputDeviceLightInfo> lights = info.getLights(); + ASSERT_EQ(1U, lights.size()); + ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS)); ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS); @@ -10020,7 +10464,85 @@ TEST_F(LightControllerTest, RGBLight) { controller.populateDeviceInfo(&info); std::vector<InputDeviceLightInfo> lights = info.getLights(); ASSERT_EQ(1U, lights.size()); - ASSERT_EQ(InputDeviceLightType::RGB, lights[0].type); + ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); + + ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); + ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightControllerTest, CorrectRGBKeyboardBacklight) { + RawLightInfo infoRed = {.id = 1, + .name = "red_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + RawLightInfo infoGreen = {.id = 2, + .name = "green_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + RawLightInfo infoBlue = {.id = 3, + .name = "blue_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed)); + mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen)); + mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue)); + + PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); + InputDeviceInfo info; + controller.populateDeviceInfo(&info); + std::vector<InputDeviceLightInfo> lights = info.getLights(); + ASSERT_EQ(1U, lights.size()); + ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); + + ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); + ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightControllerTest, IncorrectRGBKeyboardBacklight) { + RawLightInfo infoRed = {.id = 1, + .name = "red", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED, + .path = ""}; + RawLightInfo infoGreen = {.id = 2, + .name = "green", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN, + .path = ""}; + RawLightInfo infoBlue = {.id = 3, + .name = "blue", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE, + .path = ""}; + RawLightInfo infoGlobal = {.id = 3, + .name = "global_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::GLOBAL | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed)); + mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen)); + mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue)); + mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoGlobal)); + + PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); + InputDeviceInfo info; + controller.populateDeviceInfo(&info); + std::vector<InputDeviceLightInfo> lights = info.getLights(); + ASSERT_EQ(1U, lights.size()); + ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); @@ -10028,7 +10550,7 @@ TEST_F(LightControllerTest, RGBLight) { TEST_F(LightControllerTest, MultiColorRGBLight) { RawLightInfo infoColor = {.id = 1, - .name = "red", + .name = "multi_color", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS | InputLightClass::MULTI_INTENSITY | @@ -10042,7 +10564,34 @@ TEST_F(LightControllerTest, MultiColorRGBLight) { controller.populateDeviceInfo(&info); std::vector<InputDeviceLightInfo> lights = info.getLights(); ASSERT_EQ(1U, lights.size()); - ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, lights[0].type); + ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); + + ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); + ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightControllerTest, MultiColorRGBKeyboardBacklight) { + RawLightInfo infoColor = {.id = 1, + .name = "multi_color_keyboard_backlight", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | + InputLightClass::MULTI_INTENSITY | + InputLightClass::MULTI_INDEX | + InputLightClass::KEYBOARD_BACKLIGHT, + .path = ""}; + + mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor)); + + PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); + InputDeviceInfo info; + controller.populateDeviceInfo(&info); + std::vector<InputDeviceLightInfo> lights = info.getLights(); + ASSERT_EQ(1U, lights.size()); + ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); @@ -10080,6 +10629,8 @@ TEST_F(LightControllerTest, PlayerIdLight) { std::vector<InputDeviceLightInfo> lights = info.getLights(); ASSERT_EQ(1U, lights.size()); ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type); + ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID)); diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp new file mode 100644 index 0000000000..1f8cd12b75 --- /dev/null +++ b/services/inputflinger/tests/InstrumentedInputReader.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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 "InstrumentedInputReader.h" + +namespace android { + +InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, + InputListenerInterface& listener) + : InputReader(eventHub, policy, listener), mFakeContext(this) {} + +void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) { + mNextDevices.push(device); +} + +std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId, + const std::string& name, + const std::string& location) { + InputDeviceIdentifier identifier; + identifier.name = name; + identifier.location = location; + int32_t generation = deviceId + 1; + return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier); +} + +std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked( + int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) { + if (!mNextDevices.empty()) { + std::shared_ptr<InputDevice> device(std::move(mNextDevices.front())); + mNextDevices.pop(); + return device; + } + return InputReader::createDeviceLocked(eventHubId, identifier); +} + +} // namespace android diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h new file mode 100644 index 0000000000..7f8d5562ef --- /dev/null +++ b/services/inputflinger/tests/InstrumentedInputReader.h @@ -0,0 +1,123 @@ +/* + * Copyright 2022 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 <memory> +#include <queue> +#include <string> + +#include <InputDevice.h> +#include <InputReader.h> +#include <gtest/gtest.h> +#include <utils/StrongPointer.h> + +namespace android { + +class InstrumentedInputReader : public InputReader { +public: + InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, + InputListenerInterface& listener); + virtual ~InstrumentedInputReader() {} + + void pushNextDevice(std::shared_ptr<InputDevice> device); + + std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, + const std::string& location = ""); + + // Make the protected loopOnce method accessible to tests. + using InputReader::loopOnce; + +protected: + virtual std::shared_ptr<InputDevice> createDeviceLocked( + int32_t eventHubId, const InputDeviceIdentifier& identifier); + + class FakeInputReaderContext : public ContextImpl { + public: + FakeInputReaderContext(InputReader* reader) + : ContextImpl(reader), + mGlobalMetaState(0), + mUpdateGlobalMetaStateWasCalled(false), + mGeneration(1) {} + + virtual ~FakeInputReaderContext() {} + + void assertUpdateGlobalMetaStateWasCalled() { + ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) + << "Expected updateGlobalMetaState() to have been called."; + mUpdateGlobalMetaStateWasCalled = false; + } + + void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; } + + uint32_t getGeneration() { return mGeneration; } + + void updateGlobalMetaState() override { + mUpdateGlobalMetaStateWasCalled = true; + ContextImpl::updateGlobalMetaState(); + } + + int32_t getGlobalMetaState() override { + return mGlobalMetaState | ContextImpl::getGlobalMetaState(); + } + + int32_t bumpGeneration() override { + mGeneration = ContextImpl::bumpGeneration(); + return mGeneration; + } + + void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; } + + void assertTimeoutWasRequested(nsecs_t when) { + ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when + << " but there was no timeout requested."; + ASSERT_EQ(when, *mRequestedTimeout); + mRequestedTimeout.reset(); + } + + void assertTimeoutWasNotRequested() { + ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested," + " but one was requested at time " + << *mRequestedTimeout; + } + + void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override { + outDevices = mExternalStylusDevices; + } + + void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) { + mExternalStylusDevices = devices; + } + + private: + int32_t mGlobalMetaState; + bool mUpdateGlobalMetaStateWasCalled; + int32_t mGeneration; + std::optional<nsecs_t> mRequestedTimeout; + std::vector<InputDeviceInfo> mExternalStylusDevices; + } mFakeContext; + + friend class InputReaderTest; + +public: + FakeInputReaderContext* getContext() { return &mFakeContext; } + +private: + std::queue<std::shared_ptr<InputDevice>> mNextDevices; +}; + +} // namespace android diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index 89c0741dfd..1f4b2486bd 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -44,7 +44,7 @@ InputEventTimeline getTestTimeline() { graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9; graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 10; expectedCT.setGraphicsTimeline(std::move(graphicsTimeline)); - t.connectionTimelines.emplace(new BBinder(), std::move(expectedCT)); + t.connectionTimelines.emplace(sp<BBinder>::make(), std::move(expectedCT)); return t; } @@ -56,8 +56,8 @@ protected: sp<IBinder> connection2; void SetUp() override { - connection1 = new BBinder(); - connection2 = new BBinder(); + connection1 = sp<BBinder>::make(); + connection2 = sp<BBinder>::make(); mTracker = std::make_unique<LatencyTracker>(this); } diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp new file mode 100644 index 0000000000..671558509d --- /dev/null +++ b/services/inputflinger/tests/NotifyArgs_test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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 <NotifyArgs.h> +#include <utils/Timers.h> + +#include <gtest/gtest.h> +#include "android/input.h" +#include "input/Input.h" +#include "input/TouchVideoFrame.h" + +namespace android { + +// --- NotifyArgsTest --- + +/** + * Validate basic copy assignment. + */ +TEST(NotifyMotionArgsTest, TestCopyAssignmentOperator) { + int32_t id = 123; + nsecs_t downTime = systemTime(); + nsecs_t eventTime = downTime++; + nsecs_t readTime = downTime++; + int32_t deviceId = 7; + uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + int32_t displayId = 42; + uint32_t policyFlags = POLICY_FLAG_GESTURE; + int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE; + int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY; + int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + int32_t metaState = AMETA_SCROLL_LOCK_ON; + uint32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY; + MotionClassification classification = MotionClassification::DEEP_PRESS; + int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + uint32_t pointerCount = 2; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + float x = 0; + float y = 10; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x++); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y++); + } + + float xPrecision = 1.2f; + float yPrecision = 3.4f; + float xCursorPosition = 5.6f; + float yCursorPosition = 7.8f; + + std::vector<int16_t> videoData = {1, 2, 3, 4}; + timeval timestamp = {5, 6}; + TouchVideoFrame frame(2, 2, std::move(videoData), timestamp); + std::vector<TouchVideoFrame> videoFrames = {frame}; + const NotifyMotionArgs args(id, eventTime, readTime, deviceId, source, displayId, policyFlags, + action, actionButton, flags, metaState, buttonState, classification, + edgeFlags, pointerCount, pointerProperties, pointerCoords, + xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, + videoFrames); + + NotifyMotionArgs otherArgs{}; + otherArgs = args; + + EXPECT_EQ(args, otherArgs); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp index 8e2ab88e80..bd053603df 100644 --- a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp +++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp @@ -33,14 +33,8 @@ static constexpr int32_t POINTER_1_DOWN = constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS; -struct PointerData { - float x; - float y; -}; - static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action, - const std::vector<PointerData>& points, - uint32_t source) { + const std::vector<Point>& points, uint32_t source) { size_t pointerCount = points.size(); if (action == DOWN || action == UP) { EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; diff --git a/libs/gui/include/gui/TransactionTracing.h b/services/inputflinger/tests/TestConstants.h index 9efba47a18..27881f6f49 100644 --- a/libs/gui/include/gui/TransactionTracing.h +++ b/services/inputflinger/tests/TestConstants.h @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2022 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. @@ -16,26 +16,18 @@ #pragma once -#include <android/gui/BnTransactionTraceListener.h> -#include <utils/Mutex.h> - namespace android { -class TransactionTraceListener : public gui::BnTransactionTraceListener { - static std::mutex sMutex; - static sp<TransactionTraceListener> sInstance; - - TransactionTraceListener(); - -public: - static sp<TransactionTraceListener> getInstance(); +using std::chrono_literals::operator""ms; - binder::Status onToggled(bool enabled) override; +// Timeout for waiting for an expected event +static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; - bool isTracingEnabled(); +// An arbitrary time value. +static constexpr nsecs_t ARBITRARY_TIME = 1234; +static constexpr nsecs_t READ_TIME = 4321; -private: - bool mTracingEnabled = false; -}; +// Error tolerance for floating point assertions. +static const float EPSILON = 0.001f; } // namespace android diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 57b382c718..2801072b6a 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -59,26 +59,34 @@ void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) { assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called.")); } +void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) { + NotifyKeyArgs outEventArgs; + ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs)); + ASSERT_THAT(outEventArgs, matcher); +} + void TestInputListener::assertNotifyKeyWasNotCalled() { ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called.")); } -void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) { +void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs, + std::optional<TimePoint> waitUntil) { ASSERT_NO_FATAL_FAILURE( assertCalled<NotifyMotionArgs>(outEventArgs, - "Expected notifyMotion() to have been called.")); + "Expected notifyMotion() to have been called.", + waitUntil)); } void TestInputListener::assertNotifyMotionWasCalled( - const ::testing::Matcher<NotifyMotionArgs>& matcher) { + const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) { NotifyMotionArgs outEventArgs; - ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs)); + ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil)); ASSERT_THAT(outEventArgs, matcher); } -void TestInputListener::assertNotifyMotionWasNotCalled() { +void TestInputListener::assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil) { ASSERT_NO_FATAL_FAILURE( - assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.")); + assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.", waitUntil)); } void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) { @@ -113,15 +121,18 @@ void TestInputListener::assertNotifyCaptureWasNotCalled() { } template <class NotifyArgsType> -void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { +void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); if (queue.empty()) { - const bool eventReceived = - mCondition.wait_for(lock, mEventHappenedTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const auto time = + waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout); + const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (!eventReceived) { FAIL() << "Timed out waiting for event: " << message.c_str(); } @@ -133,21 +144,23 @@ void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string m } template <class NotifyArgsType> -void TestInputListener::assertNotCalled(std::string message) { +void TestInputListener::assertNotCalled(std::string message, std::optional<TimePoint> waitUntil) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); - const bool eventReceived = - mCondition.wait_for(lock, mEventDidNotHappenTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const auto time = + waitUntil.value_or(std::chrono::system_clock::now() + mEventDidNotHappenTimeout); + const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (eventReceived) { FAIL() << "Unexpected event: " << message.c_str(); } } template <class NotifyArgsType> -void TestInputListener::notify(const NotifyArgsType* args) { +void TestInputListener::addToQueue(const NotifyArgsType* args) { std::scoped_lock<std::mutex> lock(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); @@ -156,35 +169,35 @@ void TestInputListener::notify(const NotifyArgsType* args) { } void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { - notify<NotifyConfigurationChangedArgs>(args); + addToQueue<NotifyConfigurationChangedArgs>(args); } void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - notify<NotifyDeviceResetArgs>(args); + addToQueue<NotifyDeviceResetArgs>(args); } void TestInputListener::notifyKey(const NotifyKeyArgs* args) { - notify<NotifyKeyArgs>(args); + addToQueue<NotifyKeyArgs>(args); } void TestInputListener::notifyMotion(const NotifyMotionArgs* args) { - notify<NotifyMotionArgs>(args); + addToQueue<NotifyMotionArgs>(args); } void TestInputListener::notifySwitch(const NotifySwitchArgs* args) { - notify<NotifySwitchArgs>(args); + addToQueue<NotifySwitchArgs>(args); } void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { - notify<NotifyPointerCaptureChangedArgs>(args); + addToQueue<NotifyPointerCaptureChangedArgs>(args); } void TestInputListener::notifySensor(const NotifySensorArgs* args) { - notify<NotifySensorArgs>(args); + addToQueue<NotifySensorArgs>(args); } void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) { - notify<NotifyVibratorStateArgs>(args); + addToQueue<NotifyVibratorStateArgs>(args); } } // namespace android diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 0bdfc6b9fb..9665f702a1 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_TEST_INPUT_LISTENER_H -#define _UI_TEST_INPUT_LISTENER_H +#pragma once #include <android-base/thread_annotations.h> #include <gmock/gmock.h> @@ -34,6 +33,8 @@ public: std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); virtual ~TestInputListener(); + using TimePoint = std::chrono::time_point<std::chrono::system_clock>; + void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); @@ -45,13 +46,17 @@ public: void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr); + void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher); + void assertNotifyKeyWasNotCalled(); - void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr); + void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr, + std::optional<TimePoint> waitUntil = {}); - void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher); + void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher, + std::optional<TimePoint> waitUntil = {}); - void assertNotifyMotionWasNotCalled(); + void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {}); void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); @@ -62,13 +67,14 @@ public: private: template <class NotifyArgsType> - void assertCalled(NotifyArgsType* outEventArgs, std::string message); + void assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil = {}); template <class NotifyArgsType> - void assertNotCalled(std::string message); + void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {}); template <class NotifyArgsType> - void notify(const NotifyArgsType* args); + void addToQueue(const NotifyArgsType* args); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; @@ -103,4 +109,3 @@ private: }; } // namespace android -#endif diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h new file mode 100644 index 0000000000..9db34227f5 --- /dev/null +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/input.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/Input.h> + +namespace android { + +MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") { + bool matches = action == arg.action; + if (!matches) { + *result_listener << "expected action " << MotionEvent::actionToString(action) + << ", but got " << MotionEvent::actionToString(arg.action); + } + if (action == AMOTION_EVENT_ACTION_CANCEL) { + if (!matches) { + *result_listener << "; "; + } + *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set"; + matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; +} + +MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") { + *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got " + << KeyEvent::actionToString(arg.action); + return arg.action == action; +} + +MATCHER_P(WithSource, source, "InputEvent with specified source") { + *result_listener << "expected source " << inputEventSourceToString(source) << ", but got " + << inputEventSourceToString(arg.source); + return arg.source == source; +} + +MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { + *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId; + return arg.displayId == displayId; +} + +MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") { + *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId; + return arg.deviceId == deviceId; +} + +MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { + *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode; + return arg.keyCode == keyCode; +} + +MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") { + const auto argX = arg.pointerCoords[0].getX(); + const auto argY = arg.pointerCoords[0].getY(); + *result_listener << "expected coords (" << x << ", " << y << "), but got (" << argX << ", " + << argY << ")"; + return argX == x && argY == y; +} + +MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { + const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + *result_listener << "expected pressure " << pressure << ", but got " << argPressure; + return argPressure == pressure; +} + +MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") { + const auto argToolType = arg.pointerProperties[0].toolType; + *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got " + << motionToolTypeToString(argToolType); + return argToolType == toolType; +} + +MATCHER_P(WithFlags, flags, "InputEvent with specified flags") { + *result_listener << "expected flags " << flags << ", but got " << arg.flags; + return arg.flags == flags; +} + +MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { + *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState; + return arg.buttonState == buttons; +} + +MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") { + *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime; + return arg.eventTime == eventTime; +} + +} // namespace android diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 9c939198f3..97a26141e0 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -17,13 +17,15 @@ #include "UinputDevice.h" #include <android-base/stringprintf.h> +#include <cutils/memory.h> #include <fcntl.h> namespace android { // --- UinputDevice --- -UinputDevice::UinputDevice(const char* name) : mName(name) {} +UinputDevice::UinputDevice(const char* name, int16_t productId) + : mName(name), mProductId(productId) {} UinputDevice::~UinputDevice() { if (ioctl(mDeviceFd, UI_DEV_DESTROY)) { @@ -42,7 +44,7 @@ void UinputDevice::init() { strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE); device.id.bustype = BUS_USB; device.id.vendor = 0x01; - device.id.product = 0x01; + device.id.product = mProductId; device.id.version = 1; ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); @@ -58,11 +60,11 @@ void UinputDevice::init() { } void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { + // uinput ignores the timestamp struct input_event event = {}; event.type = type; event.code = code; event.value = value; - event.time = {}; // uinput ignores the timestamp if (write(mDeviceFd, &event, sizeof(input_event)) < 0) { std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16 @@ -75,8 +77,8 @@ void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { // --- UinputKeyboard --- -UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys) - : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {} +UinputKeyboard::UinputKeyboard(const char* name, int16_t productId, std::initializer_list<int> keys) + : UinputDevice(name, productId), mKeys(keys.begin(), keys.end()) {} void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { // enable key press/release event @@ -120,22 +122,52 @@ void UinputKeyboard::pressAndReleaseKey(int key) { // --- UinputHomeKey --- -UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} +UinputHomeKey::UinputHomeKey() : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {KEY_HOME}) {} void UinputHomeKey::pressAndReleaseHomeKey() { pressAndReleaseKey(KEY_HOME); } -// --- UinputSteamController -UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {} +// --- UinputSteamController --- + +UinputSteamController::UinputSteamController() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_GEAR_DOWN, BTN_GEAR_UP}) {} + +// --- UinputExternalStylus --- + +UinputExternalStylus::UinputExternalStylus() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} + +// --- UinputExternalStylusWithPressure --- + +UinputExternalStylusWithPressure::UinputExternalStylusWithPressure() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} + +void UinputExternalStylusWithPressure::configureDevice(int fd, uinput_user_dev* device) { + UinputKeyboard::configureDevice(fd, device); + + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE); + device->absmin[ABS_PRESSURE] = RAW_PRESSURE_MIN; + device->absmax[ABS_PRESSURE] = RAW_PRESSURE_MAX; +} + +void UinputExternalStylusWithPressure::setPressure(int32_t pressure) { + injectEvent(EV_ABS, ABS_PRESSURE, pressure); + injectEvent(EV_SYN, SYN_REPORT, 0); +} // --- UinputTouchScreen --- -UinputTouchScreen::UinputTouchScreen(const Rect* size) - : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} + +UinputTouchScreen::UinputTouchScreen(const Rect& size) + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, + {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}), + mSize(size) {} void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { + UinputKeyboard::configureDevice(fd, device); + // Setup the touch screen device - ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_ABS); ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); @@ -145,7 +177,6 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); - ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; @@ -157,6 +188,8 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1; device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN; device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX; + device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER; + device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX; } void UinputTouchScreen::sendSlot(int32_t slot) { diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index a37fc2b790..51e331df8f 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H -#define _UI_TEST_INPUT_UINPUT_INJECTOR_H +#pragma once #include <android-base/unique_fd.h> #include <gtest/gtest.h> @@ -33,7 +32,7 @@ namespace android { template <class D, class... Ts> std::unique_ptr<D> createUinputDevice(Ts... args) { // Using `new` to access non-public constructors. - std::unique_ptr<D> dev(new D(&args...)); + std::unique_ptr<D> dev(new D(args...)); EXPECT_NO_FATAL_FAILURE(dev->init()); return dev; } @@ -52,8 +51,9 @@ public: protected: const char* mName; + const int16_t mProductId; - UinputDevice(const char* name); + explicit UinputDevice(const char* name, int16_t productId); // Signals which types of events this device supports before it is created. // This must be overridden by subclasses. @@ -72,7 +72,8 @@ private: class UinputKeyboard : public UinputDevice { public: - static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device"; + static constexpr const char* KEYBOARD_NAME = "Test Uinput Keyboard Device"; + static constexpr int16_t PRODUCT_ID = 42; // Injects key press and sync. void pressKey(int key); @@ -85,11 +86,12 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); protected: - UinputKeyboard(std::initializer_list<int> keys = {}); + explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID, + std::initializer_list<int> keys = {}); -private: void configureDevice(int fd, uinput_user_dev* device) override; +private: std::set<int> mKeys; }; @@ -98,6 +100,9 @@ private: // A keyboard device that has a single HOME key. class UinputHomeKey : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Home Key"; + static constexpr int16_t PRODUCT_ID = 43; + // Injects 4 events: key press, sync, key release, and sync. void pressAndReleaseHomeKey(); @@ -105,24 +110,69 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputHomeKey(); + explicit UinputHomeKey(); }; +// --- UinputSteamController --- + // A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key. class UinputSteamController : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Steam Controller"; + static constexpr int16_t PRODUCT_ID = 44; + + template <class D, class... Ts> + friend std::unique_ptr<D> createUinputDevice(Ts... args); + +private: + explicit UinputSteamController(); +}; + +// --- UinputExternalStylus --- + +// A stylus that reports button presses. +class UinputExternalStylus : public UinputKeyboard { +public: + static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus"; + static constexpr int16_t PRODUCT_ID = 45; + + template <class D, class... Ts> + friend std::unique_ptr<D> createUinputDevice(Ts... args); + +private: + explicit UinputExternalStylus(); +}; + +// --- UinputExternalStylusWithPressure --- + +// A stylus that reports button presses and pressure values. +class UinputExternalStylusWithPressure : public UinputKeyboard { +public: + static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus With Pressure"; + static constexpr int16_t PRODUCT_ID = 46; + + static constexpr int32_t RAW_PRESSURE_MIN = 0; + static constexpr int32_t RAW_PRESSURE_MAX = 255; + + void setPressure(int32_t pressure); + template <class D, class... Ts> friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputSteamController(); + void configureDevice(int fd, uinput_user_dev* device) override; + + explicit UinputExternalStylusWithPressure(); }; // --- UinputTouchScreen --- -// A touch screen device with specific size. -class UinputTouchScreen : public UinputDevice { + +// A multi-touch touchscreen device with specific size that also supports styluses. +class UinputTouchScreen : public UinputKeyboard { public: - static constexpr const char* DEVICE_NAME = "Test Touch Screen"; + static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen"; + static constexpr int16_t PRODUCT_ID = 47; + static const int32_t RAW_TOUCH_MIN = 0; static const int32_t RAW_TOUCH_MAX = 31; static const int32_t RAW_ID_MIN = 0; @@ -147,7 +197,7 @@ public: const Point getCenterPoint(); protected: - UinputTouchScreen(const Rect* size); + explicit UinputTouchScreen(const Rect& size); private: void configureDevice(int fd, uinput_user_dev* device) override; @@ -155,5 +205,3 @@ private: }; } // namespace android - -#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index 29fa001185..4c84160a1d 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -24,6 +24,7 @@ #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" #include "TestInputListener.h" +#include "TestInputListenerMatchers.h" using ::testing::AllOf; @@ -55,21 +56,6 @@ constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED; -MATCHER_P(WithAction, action, "MotionEvent with specified action") { - bool result = true; - if (action == CANCEL) { - result &= (arg.flags & FLAG_CANCELED) != 0; - } - result &= arg.action == action; - *result_listener << "expected to receive " << MotionEvent::actionToString(action) - << " but received " << MotionEvent::actionToString(arg.action) << " instead."; - return result; -} - -MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") { - return arg.flags == flags; -} - static nsecs_t toNs(std::chrono::nanoseconds duration) { return duration.count(); } @@ -428,11 +414,11 @@ protected: }; /** - * Create a basic configuration change and send it to input classifier. + * Create a basic configuration change and send it to input processor. * Expect that the event is received by the next input stage, unmodified. */ TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { - // Create a basic configuration change and send to classifier + // Create a basic configuration change and send to blocker NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); mBlocker->notifyConfigurationChanged(&args); @@ -446,7 +432,7 @@ TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListene * to next stage unmodified. */ TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) { - // Create a basic key event and send to classifier + // Create a basic key event and send to blocker NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, @@ -605,19 +591,19 @@ TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) { // Small touch down NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); mBlocker->notifyMotion(&args1); - mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN)); // Large touch oval on the next move NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}); mBlocker->notifyMotion(&args2); - mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE)); // Lift up the touch to force the model to decide on whether it's a palm NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}); mBlocker->notifyMotion(&args3); - mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(CANCEL)); } /** @@ -633,14 +619,14 @@ TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) { NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args1); - mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN)); // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}); args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args2); - mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE)); // Lift up the stylus. If it were a touch event, this would force the model to decide on whether // it's a palm. @@ -648,7 +634,7 @@ TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) { generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}); args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args3); - mTestListener.assertNotifyMotionWasCalled(WithAction(UP)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP)); } /** @@ -664,21 +650,21 @@ TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) { // Touch down NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); mBlocker->notifyMotion(&args1); - mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN)); // Stylus pointer down NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN, {{1, 2, 3}, {10, 20, 30}}); args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args2); - mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(POINTER_1_DOWN)); // Large touch oval on the next finger move NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE, {{1, 2, 300}, {11, 21, 30}}); args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args3); - mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE)); // Lift up the finger pointer. It should be canceled due to the heuristic filter. NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP, @@ -686,14 +672,14 @@ TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) { args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args4); mTestListener.assertNotifyMotionWasCalled( - AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED))); + AllOf(WithMotionAction(POINTER_0_UP), WithFlags(FLAG_CANCELED))); NotifyMotionArgs args5 = generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}}); args5.pointerProperties[0].id = args4.pointerProperties[1].id; args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args5); - mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE)); // Lift up the stylus pointer NotifyMotionArgs args6 = @@ -701,7 +687,7 @@ TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) { args6.pointerProperties[0].id = args4.pointerProperties[1].id; args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mBlocker->notifyMotion(&args6); - mTestListener.assertNotifyMotionWasCalled(WithAction(UP)); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP)); } using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index 455a1e2133..55c2db6c91 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -21,7 +21,6 @@ package { default_applicable_licenses: ["frameworks_native_license"], } - cc_fuzz { name: "inputflinger_latencytracker_fuzzer", defaults: [ @@ -34,7 +33,6 @@ cc_fuzz { "libbase", "libbinder", "liblog", - "libui", "libutils", "libinput", "libinputflinger", @@ -43,6 +41,107 @@ cc_fuzz { "LatencyTrackerFuzzer.cpp", ], fuzz_config: { - cc: ["android-framework-input@google.com"], + cc: ["android-framework-input@google.com"], }, } + +cc_defaults { + name: "inputflinger_fuzz_defaults", + defaults: [ + "inputflinger_defaults", + ], + include_dirs: [ + "frameworks/native/services/inputflinger", + ], + shared_libs: [ + "android.hardware.input.classifier@1.0", + "android.hardware.input.processor-V1-ndk", + "libbase", + "libbinder", + "libcutils", + "liblog", + "libutils", + "libinput", + "libinputflinger", + "libinputreader", + "libinputflinger_base", + "libstatslog", + ], + header_libs: [ + "libbatteryservice_headers", + "libinputreader_headers", + ], + fuzz_config: { + cc: ["android-framework-input@google.com"], + }, +} + +cc_fuzz { + name: "inputflinger_cursor_input_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "CursorInputFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_keyboard_input_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "KeyboardInputFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_multitouch_input_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "MultiTouchInputFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_switch_input_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "SwitchInputFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_input_reader_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "InputReaderFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_blocking_queue_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "BlockingQueueFuzzer.cpp", + ], +} + +cc_fuzz { + name: "inputflinger_input_classifier_fuzzer", + defaults: [ + "inputflinger_fuzz_defaults", + ], + srcs: [ + "InputClassifierFuzzer.cpp", + ], +} diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp new file mode 100644 index 0000000000..d2595bfc9d --- /dev/null +++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2022 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 <fuzzer/FuzzedDataProvider.h> +#include <thread> +#include "BlockingQueue.h" + +// Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory. +static constexpr size_t MAX_CAPACITY = 1024; + +namespace android { + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + size_t capacity = fdp.ConsumeIntegralInRange<size_t>(1, MAX_CAPACITY); + size_t filled = 0; + BlockingQueue<int32_t> queue(capacity); + + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + size_t numPushes = fdp.ConsumeIntegralInRange<size_t>(0, capacity + 1); + for (size_t i = 0; i < numPushes; i++) { + queue.push(fdp.ConsumeIntegral<int32_t>()); + } + filled = std::min(capacity, filled + numPushes); + }, + [&]() -> void { + // Pops blocks if it is empty, so only pop up to num elements inserted. + size_t numPops = fdp.ConsumeIntegralInRange<size_t>(0, filled); + for (size_t i = 0; i < numPops; i++) { + queue.pop(); + } + filled > numPops ? filled -= numPops : filled = 0; + }, + [&]() -> void { + queue.clear(); + filled = 0; + }, + [&]() -> void { + int32_t eraseElement = fdp.ConsumeIntegral<int32_t>(); + queue.erase([&](int32_t element) { + if (element == eraseElement) { + filled--; + return true; + } + return false; + }); + }, + })(); + } + + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp new file mode 100644 index 0000000000..cc523e12cd --- /dev/null +++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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 <CursorInputMapper.h> +#include <FuzzContainer.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) { + // Pick a random property to set for the mapper to have set. + fdp->PickValueInArray<std::function<void()>>( + {[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); }, + [&]() -> void { fuzzer.addProperty("cursor.mode", "navigation"); }, + [&]() -> void { + fuzzer.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data()); + }, + [&]() -> void { + fuzzer.addProperty("cursor.orientationAware", + fdp->ConsumeRandomLengthString(100).data()); + }})(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size); + FuzzContainer fuzzer(fdp); + + CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(); + auto policyConfig = fuzzer.getPolicyConfig(); + + // Loop through mapper operations until randomness is exhausted. + while (fdp->remaining_bytes() > 0) { + fdp->PickValueInArray<std::function<void()>>({ + [&]() -> void { addProperty(fuzzer, fdp); }, + [&]() -> void { + std::string dump; + mapper.dump(dump); + }, + [&]() -> void { mapper.getSources(); }, + [&]() -> void { + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + // Need to reconfigure with 0 or you risk a NPE. + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + }, + [&]() -> void { + int32_t type, code; + type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) + : fdp->ConsumeIntegral<int32_t>(); + code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) + : fdp->ConsumeIntegral<int32_t>(); + + // Need to reconfigure with 0 or you risk a NPE. + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0); + RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<int32_t>(), + type, + code, + fdp->ConsumeIntegral<int32_t>()}; + unused += mapper.process(&rawEvent); + }, + [&]() -> void { + std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); + }, + [&]() -> void { + mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + // Need to reconfigure with 0 or you risk a NPE. + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0); + mapper.getAssociatedDisplayId(); + }, + })(); + } + + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h new file mode 100644 index 0000000000..1e0764f10f --- /dev/null +++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h @@ -0,0 +1,85 @@ +/* + * Copyright 2022 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 <InputDevice.h> +#include <InputMapper.h> +#include <InputReader.h> +#include <MapperHelpers.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +class FuzzContainer { + std::shared_ptr<FuzzEventHub> mFuzzEventHub; + sp<FuzzInputReaderPolicy> mFuzzPolicy; + FuzzInputListener mFuzzListener; + std::unique_ptr<FuzzInputReaderContext> mFuzzContext; + std::unique_ptr<InputDevice> mFuzzDevice; + InputReaderConfiguration mPolicyConfig; + std::shared_ptr<FuzzedDataProvider> mFdp; + +public: + FuzzContainer(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(fdp) { + // Setup parameters. + std::string deviceName = mFdp->ConsumeRandomLengthString(16); + std::string deviceLocation = mFdp->ConsumeRandomLengthString(12); + int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5); + int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5); + + // Create mocked objects. + mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp); + mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp); + mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy, + mFuzzListener, mFdp); + + InputDeviceIdentifier identifier; + identifier.name = deviceName; + identifier.location = deviceLocation; + mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration, + identifier); + mFuzzPolicy->getReaderConfiguration(&mPolicyConfig); + } + + ~FuzzContainer() {} + + void configureDevice() { + nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>(); + std::list<NotifyArgs> out; + out += mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0); + out += mFuzzDevice->reset(arbitraryTime); + for (const NotifyArgs& args : out) { + mFuzzListener.notify(args); + } + } + + void addProperty(std::string key, std::string value) { + mFuzzEventHub->addProperty(key, value); + configureDevice(); + } + + InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; } + + template <class T, typename... Args> + T& getMapper(Args... args) { + T& mapper = mFuzzDevice->addMapper<T>(mFdp->ConsumeIntegral<int32_t>(), args...); + configureDevice(); + return mapper; + } +}; + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp new file mode 100644 index 0000000000..c407cffdbf --- /dev/null +++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp @@ -0,0 +1,126 @@ +/* + * Copyright 2022 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 <MapperHelpers.h> +#include <fuzzer/FuzzedDataProvider.h> +#include "InputCommonConverter.h" +#include "InputProcessor.h" + +namespace android { + +static constexpr int32_t MAX_AXES = 64; + +// Used by two fuzz operations and a bit lengthy, so pulled out into a function. +NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) { + // Create a basic motion event for testing + PointerProperties properties; + properties.id = 0; + properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + PointerCoords coords; + coords.clear(); + for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) { + coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>()); + } + + const nsecs_t downTime = 2; + const nsecs_t readTime = downTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8); + NotifyMotionArgs motionArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/, + downTime /*eventTime*/, readTime, + fdp.ConsumeIntegral<int32_t>() /*deviceId*/, AINPUT_SOURCE_ANY, + ADISPLAY_ID_DEFAULT, + fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, + fdp.ConsumeIntegral<int32_t>() /*actionButton*/, + fdp.ConsumeIntegral<int32_t>() /*flags*/, AMETA_NONE, + fdp.ConsumeIntegral<int32_t>() /*buttonState*/, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + 1 /*pointerCount*/, &properties, &coords, + fdp.ConsumeFloatingPoint<float>() /*xPrecision*/, + fdp.ConsumeFloatingPoint<float>() /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); + return motionArgs; +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + + std::unique_ptr<FuzzInputListener> mFuzzListener = std::make_unique<FuzzInputListener>(); + std::unique_ptr<InputProcessorInterface> mClassifier = + std::make_unique<InputProcessor>(*mFuzzListener); + + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + // SendToNextStage_NotifyConfigurationChangedArgs + NotifyConfigurationChangedArgs + args(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/, + fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/); + mClassifier->notifyConfigurationChanged(&args); + }, + [&]() -> void { + // SendToNextStage_NotifyKeyArgs + const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); + const nsecs_t readTime = + eventTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8); + NotifyKeyArgs keyArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/, + eventTime, readTime, + fdp.ConsumeIntegral<int32_t>() /*deviceId*/, + AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, + fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/, + AKEY_EVENT_ACTION_DOWN, + fdp.ConsumeIntegral<int32_t>() /*flags*/, AKEYCODE_HOME, + fdp.ConsumeIntegral<int32_t>() /*scanCode*/, AMETA_NONE, + fdp.ConsumeIntegral<nsecs_t>() /*downTime*/); + + mClassifier->notifyKey(&keyArgs); + }, + [&]() -> void { + // SendToNextStage_NotifyMotionArgs + NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp); + mClassifier->notifyMotion(&motionArgs); + }, + [&]() -> void { + // SendToNextStage_NotifySwitchArgs + NotifySwitchArgs switchArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/, + fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/, + fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/, + fdp.ConsumeIntegral<uint32_t>() /*switchValues*/, + fdp.ConsumeIntegral<uint32_t>() /*switchMask*/); + + mClassifier->notifySwitch(&switchArgs); + }, + [&]() -> void { + // SendToNextStage_NotifyDeviceResetArgs + NotifyDeviceResetArgs resetArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/, + fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/, + fdp.ConsumeIntegral<int32_t>() /*deviceId*/); + + mClassifier->notifyDeviceReset(&resetArgs); + }, + [&]() -> void { + // InputClassifierConverterTest + const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp); + aidl::android::hardware::input::common::MotionEvent motionEvent = + notifyMotionArgsToHalMotionEvent(motionArgs); + }, + })(); + } + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp new file mode 100644 index 0000000000..057c15d6b8 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2022 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 <InputReader.h> +#include <MapperHelpers.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <input/InputDevice.h> +#include <chrono> +#include <thread> + +namespace android { + +constexpr InputDeviceSensorType kInputDeviceSensorType[] = { + InputDeviceSensorType::ACCELEROMETER, + InputDeviceSensorType::MAGNETIC_FIELD, + InputDeviceSensorType::ORIENTATION, + InputDeviceSensorType::GYROSCOPE, + InputDeviceSensorType::LIGHT, + InputDeviceSensorType::PRESSURE, + InputDeviceSensorType::TEMPERATURE, + InputDeviceSensorType::PROXIMITY, + InputDeviceSensorType::GRAVITY, + InputDeviceSensorType::LINEAR_ACCELERATION, + InputDeviceSensorType::ROTATION_VECTOR, + InputDeviceSensorType::RELATIVE_HUMIDITY, + InputDeviceSensorType::AMBIENT_TEMPERATURE, + InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED, + InputDeviceSensorType::GAME_ROTATION_VECTOR, + InputDeviceSensorType::GYROSCOPE_UNCALIBRATED, + InputDeviceSensorType::SIGNIFICANT_MOTION, +}; + +class FuzzInputReader : public InputReaderInterface { +public: + FuzzInputReader(std::shared_ptr<EventHubInterface> fuzzEventHub, + const sp<InputReaderPolicyInterface>& fuzzPolicy, + InputListenerInterface& fuzzListener) { + reader = std::make_unique<InputReader>(fuzzEventHub, fuzzPolicy, fuzzListener); + } + + void dump(std::string& dump) { reader->dump(dump); } + + void monitor() { reader->monitor(); } + + bool isInputDeviceEnabled(int32_t deviceId) { return reader->isInputDeviceEnabled(deviceId); } + + status_t start() { return reader->start(); } + + status_t stop() { return reader->stop(); } + + std::vector<InputDeviceInfo> getInputDevices() const { return reader->getInputDevices(); } + + int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) { + return reader->getScanCodeState(deviceId, sourceMask, scanCode); + } + + int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) { + return reader->getKeyCodeState(deviceId, sourceMask, keyCode); + } + + int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { + return reader->getSwitchState(deviceId, sourceMask, sw); + } + + void toggleCapsLockState(int32_t deviceId) { reader->toggleCapsLockState(deviceId); } + + bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) { + return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags); + } + + void requestRefreshConfiguration(uint32_t changes) { + reader->requestRefreshConfiguration(changes); + } + + void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat, + int32_t token) { + reader->vibrate(deviceId, sequence, repeat, token); + } + + void cancelVibrate(int32_t deviceId, int32_t token) { reader->cancelVibrate(deviceId, token); } + + bool isVibrating(int32_t deviceId) { return reader->isVibrating(deviceId); } + + std::vector<int32_t> getVibratorIds(int32_t deviceId) { + return reader->getVibratorIds(deviceId); + } + + std::optional<int32_t> getBatteryCapacity(int32_t deviceId) { + return reader->getBatteryCapacity(deviceId); + } + + std::optional<int32_t> getBatteryStatus(int32_t deviceId) { + return reader->getBatteryStatus(deviceId); + } + + std::optional<std::string> getBatteryDevicePath(int32_t deviceId) { + return reader->getBatteryDevicePath(deviceId); + } + + std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) { + return reader->getLights(deviceId); + } + + std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) { + return reader->getSensors(deviceId); + } + + bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) { + return reader->canDispatchToDisplay(deviceId, displayId); + } + + bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType, + std::chrono::microseconds samplingPeriod, + std::chrono::microseconds maxBatchReportLatency) { + return reader->enableSensor(deviceId, sensorType, samplingPeriod, maxBatchReportLatency); + } + + void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) { + return reader->disableSensor(deviceId, sensorType); + } + + void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) { + return reader->flushSensor(deviceId, sensorType); + } + + bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) { + return reader->setLightColor(deviceId, lightId, color); + } + + bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) { + return reader->setLightPlayerId(deviceId, lightId, playerId); + } + + std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) { + return reader->getLightColor(deviceId, lightId); + } + + std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) { + return reader->getLightPlayerId(deviceId, lightId); + } + + void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { + reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode); + } + + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { + return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode); + } + + std::optional<std::string> getBluetoothAddress(int32_t deviceId) const { + return reader->getBluetoothAddress(deviceId); + } + +private: + std::unique_ptr<InputReaderInterface> reader; +}; + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size); + + FuzzInputListener fuzzListener; + sp<FuzzInputReaderPolicy> fuzzPolicy = sp<FuzzInputReaderPolicy>::make(fdp); + std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp); + std::unique_ptr<FuzzInputReader> reader = + std::make_unique<FuzzInputReader>(fuzzEventHub, fuzzPolicy, fuzzListener); + size_t patternCount = fdp->ConsumeIntegralInRange<size_t>(1, 260); + VibrationSequence pattern(patternCount); + for (size_t i = 0; i < patternCount; ++i) { + VibrationElement element(i); + element.addChannel(fdp->ConsumeIntegral<int32_t>() /* vibratorId */, + fdp->ConsumeIntegral<uint8_t>() /* amplitude */); + pattern.addElement(element); + } + reader->vibrate(fdp->ConsumeIntegral<int32_t>(), pattern, + fdp->ConsumeIntegral<ssize_t>() /*repeat*/, + fdp->ConsumeIntegral<int32_t>() /*token*/); + reader->start(); + + // Loop through mapper operations until randomness is exhausted. + while (fdp->remaining_bytes() > 0) { + fdp->PickValueInArray<std::function<void()>>({ + [&]() -> void { + std::string dump; + reader->dump(dump); + }, + [&]() -> void { reader->monitor(); }, + [&]() -> void { reader->getInputDevices(); }, + [&]() -> void { reader->isInputDeviceEnabled(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { + reader->getScanCodeState(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->getKeyCodeState(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->getSwitchState(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { + size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024); + std::vector<uint8_t> outFlags(count); + std::vector<int32_t> keyCodes; + for (size_t i = 0; i < count; ++i) { + keyCodes.push_back(fdp->ConsumeIntegral<int32_t>()); + } + reader->hasKeys(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<uint32_t>(), keyCodes, outFlags.data()); + }, + [&]() -> void { + reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>()); + }, + [&]() -> void { + reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->canDispatchToDisplay(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->getKeyCodeForKeyLocation(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { reader->getBatteryCapacity(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->getBatteryStatus(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->getBatteryDevicePath(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->getLights(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->getSensors(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { + reader->getLightPlayerId(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->getLightColor(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->setLightPlayerId(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->setLightColor(fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + reader->flushSensor(fdp->ConsumeIntegral<int32_t>(), + fdp->PickValueInArray<InputDeviceSensorType>( + kInputDeviceSensorType)); + }, + [&]() -> void { + reader->disableSensor(fdp->ConsumeIntegral<int32_t>(), + fdp->PickValueInArray<InputDeviceSensorType>( + kInputDeviceSensorType)); + }, + [&]() -> void { + reader->enableSensor(fdp->ConsumeIntegral<int32_t>(), + fdp->PickValueInArray<InputDeviceSensorType>( + kInputDeviceSensorType), + std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()), + std::chrono::microseconds(fdp->ConsumeIntegral<size_t>())); + }, + [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); }, + })(); + } + + reader->stop(); + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp new file mode 100644 index 0000000000..e880f55891 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2022 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 <FuzzContainer.h> +#include <KeyboardInputMapper.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +const int32_t kMaxKeycodes = 100; + +static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) { + // Pick a random property to set for the mapper to have set. + fdp->PickValueInArray<std::function<void()>>( + {[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); }, + [&]() -> void { + fuzzer.addProperty("keyboard.orientationAware", + fdp->ConsumeRandomLengthString(100).data()); + }, + [&]() -> void { + fuzzer.addProperty("keyboard.doNotWakeByDefault", + fdp->ConsumeRandomLengthString(100).data()); + }, + [&]() -> void { + fuzzer.addProperty("keyboard.handlesKeyRepeat", + fdp->ConsumeRandomLengthString(100).data()); + }})(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size); + FuzzContainer fuzzer(fdp); + + KeyboardInputMapper& mapper = + fuzzer.getMapper<KeyboardInputMapper>(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + auto policyConfig = fuzzer.getPolicyConfig(); + + // Loop through mapper operations until randomness is exhausted. + while (fdp->remaining_bytes() > 0) { + fdp->PickValueInArray<std::function<void()>>({ + [&]() -> void { addProperty(fuzzer, fdp); }, + [&]() -> void { + std::string dump; + mapper.dump(dump); + }, + [&]() -> void { + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + }, + [&]() -> void { mapper.getSources(); }, + [&]() -> void { + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, + fdp->ConsumeIntegral<uint32_t>()); + }, + [&]() -> void { + std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); + }, + [&]() -> void { + int32_t type, code; + type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) + : fdp->ConsumeIntegral<int32_t>(); + code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) + : fdp->ConsumeIntegral<int32_t>(); + RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<int32_t>(), + type, + code, + fdp->ConsumeIntegral<int32_t>()}; + std::list<NotifyArgs> unused = mapper.process(&rawEvent); + }, + [&]() -> void { + mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + std::vector<int32_t> keyCodes; + int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes); + for (int32_t i = 0; i < numBytes; ++i) { + keyCodes.push_back(fdp->ConsumeIntegral<int32_t>()); + } + mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes, + nullptr); + }, + [&]() -> void { mapper.getMetaState(); }, + [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { mapper.getAssociatedDisplayId(); }, + })(); + } + + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 4f066ad513..72780fb363 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -42,7 +42,7 @@ static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, if (useExistingToken) { return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)]; } - return new BBinder(); + return sp<BBinder>::make(); } extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { @@ -54,7 +54,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; for (size_t i = 0; i < predefinedTokens.size(); i++) { - predefinedTokens[i] = new BBinder(); + predefinedTokens[i] = sp<BBinder>::make(); } // Randomly invoke LatencyTracker api's until randomness is exhausted. diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h new file mode 100644 index 0000000000..cd852d668c --- /dev/null +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -0,0 +1,371 @@ +/* + * Copyright 2022 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 <InputDevice.h> +#include <InputMapper.h> +#include <InputReader.h> +#include <fuzzer/FuzzedDataProvider.h> +#include "android/hardware/input/InputDeviceCountryCode.h" + +using android::hardware::input::InputDeviceCountryCode; + +constexpr size_t kValidTypes[] = {EV_SW, + EV_SYN, + SYN_REPORT, + EV_ABS, + EV_KEY, + EV_MSC, + EV_REL, + android::EventHubInterface::DEVICE_ADDED, + android::EventHubInterface::DEVICE_REMOVED, + android::EventHubInterface::FINISHED_DEVICE_SCAN}; + +constexpr size_t kValidCodes[] = { + SYN_REPORT, + ABS_MT_SLOT, + SYN_MT_REPORT, + ABS_MT_POSITION_X, + ABS_MT_POSITION_Y, + ABS_MT_TOUCH_MAJOR, + ABS_MT_TOUCH_MINOR, + ABS_MT_WIDTH_MAJOR, + ABS_MT_WIDTH_MINOR, + ABS_MT_ORIENTATION, + ABS_MT_TRACKING_ID, + ABS_MT_PRESSURE, + ABS_MT_DISTANCE, + ABS_MT_TOOL_TYPE, + SYN_MT_REPORT, + MSC_SCAN, + REL_X, + REL_Y, + REL_WHEEL, + REL_HWHEEL, + BTN_LEFT, + BTN_RIGHT, + BTN_MIDDLE, + BTN_BACK, + BTN_SIDE, + BTN_FORWARD, + BTN_EXTRA, + BTN_TASK, +}; + +constexpr InputDeviceCountryCode kCountryCodes[] = { + InputDeviceCountryCode::INVALID, + InputDeviceCountryCode::NOT_SUPPORTED, + InputDeviceCountryCode::ARABIC, + InputDeviceCountryCode::BELGIAN, + InputDeviceCountryCode::CANADIAN_BILINGUAL, + InputDeviceCountryCode::CANADIAN_FRENCH, + InputDeviceCountryCode::CZECH_REPUBLIC, + InputDeviceCountryCode::DANISH, + InputDeviceCountryCode::FINNISH, + InputDeviceCountryCode::FRENCH, + InputDeviceCountryCode::GERMAN, + InputDeviceCountryCode::GREEK, + InputDeviceCountryCode::HEBREW, + InputDeviceCountryCode::HUNGARY, + InputDeviceCountryCode::INTERNATIONAL, + InputDeviceCountryCode::ITALIAN, + InputDeviceCountryCode::JAPAN, + InputDeviceCountryCode::KOREAN, + InputDeviceCountryCode::LATIN_AMERICAN, + InputDeviceCountryCode::DUTCH, + InputDeviceCountryCode::NORWEGIAN, + InputDeviceCountryCode::PERSIAN, + InputDeviceCountryCode::POLAND, + InputDeviceCountryCode::PORTUGUESE, + InputDeviceCountryCode::RUSSIA, + InputDeviceCountryCode::SLOVAKIA, + InputDeviceCountryCode::SPANISH, + InputDeviceCountryCode::SWEDISH, + InputDeviceCountryCode::SWISS_FRENCH, + InputDeviceCountryCode::SWISS_GERMAN, + InputDeviceCountryCode::SWITZERLAND, + InputDeviceCountryCode::TAIWAN, + InputDeviceCountryCode::TURKISH_Q, + InputDeviceCountryCode::UK, + InputDeviceCountryCode::US, + InputDeviceCountryCode::YUGOSLAVIA, + InputDeviceCountryCode::TURKISH_F, +}; + +constexpr size_t kMaxSize = 256; + +namespace android { + +class FuzzEventHub : public EventHubInterface { + InputDeviceIdentifier mIdentifier; + std::vector<TouchVideoFrame> mVideoFrames; + PropertyMap mFuzzConfig; + std::shared_ptr<FuzzedDataProvider> mFdp; + +public: + FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {} + ~FuzzEventHub() {} + void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); } + + ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override { + return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>()); + } + InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override { + return mIdentifier; + } + int32_t getDeviceControllerNumber(int32_t deviceId) const override { + return mFdp->ConsumeIntegral<int32_t>(); + } + void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override { + *outConfiguration = mFuzzConfig; + } + status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const override { + return mFdp->ConsumeIntegral<status_t>(); + } + bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); } + bool hasInputProperty(int32_t deviceId, int property) const override { + return mFdp->ConsumeBool(); + } + bool hasMscEvent(int32_t deviceId, int mscEvent) const override { return mFdp->ConsumeBool(); } + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override { + return mFdp->ConsumeIntegral<status_t>(); + } + status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const override { + return mFdp->ConsumeIntegral<status_t>(); + } + void setExcludedDevices(const std::vector<std::string>& devices) override {} + std::vector<RawEvent> getEvents(int timeoutMillis) override { + std::vector<RawEvent> events; + const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize); + for (size_t i = 0; i < count; ++i) { + int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes) + : mFdp->ConsumeIntegral<int32_t>(); + int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes) + : mFdp->ConsumeIntegral<int32_t>(); + events.push_back({ + .when = mFdp->ConsumeIntegral<nsecs_t>(), + .readTime = mFdp->ConsumeIntegral<nsecs_t>(), + .deviceId = mFdp->ConsumeIntegral<int32_t>(), + .type = type, + .code = code, + .value = mFdp->ConsumeIntegral<int32_t>(), + }); + } + return events; + } + std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; } + + base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor( + int32_t deviceId, int32_t absCode) const override { + return base::ResultError("Fuzzer", UNKNOWN_ERROR); + }; + // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node, + // containing the raw info of the sysfs node structure. + std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override { return {}; } + std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, + int32_t BatteryId) const override { + return std::nullopt; + }; + + std::vector<int32_t> getRawLightIds(int32_t deviceId) const override { return {}; }; + std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override { + return std::nullopt; + }; + std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override { + return std::nullopt; + }; + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override{}; + std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t deviceId, int32_t lightId) const override { + return std::nullopt; + }; + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) override{}; + + InputDeviceCountryCode getCountryCode(int32_t deviceId) const override { + return mFdp->PickValueInArray<InputDeviceCountryCode>(kCountryCodes); + }; + + int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override { + return mFdp->ConsumeIntegral<int32_t>(); + } + int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override { + return mFdp->ConsumeIntegral<int32_t>(); + } + int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { + return mFdp->ConsumeIntegral<int32_t>(); + } + void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {} + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { + return mFdp->ConsumeIntegral<int32_t>(); + } + status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const override { + return mFdp->ConsumeIntegral<status_t>(); + } + bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, + uint8_t* outFlags) const override { + return mFdp->ConsumeBool(); + } + bool hasScanCode(int32_t deviceId, int32_t scanCode) const override { + return mFdp->ConsumeBool(); + } + bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override { + return mFdp->ConsumeBool(); + } + bool hasLed(int32_t deviceId, int32_t led) const override { return mFdp->ConsumeBool(); } + void setLedState(int32_t deviceId, int32_t led, bool on) override {} + void getVirtualKeyDefinitions( + int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {} + const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override { + return nullptr; + } + bool setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) override { + return mFdp->ConsumeBool(); + } + void vibrate(int32_t deviceId, const VibrationElement& effect) override {} + void cancelVibrate(int32_t deviceId) override {} + + std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return {}; }; + + /* Query battery level. */ + std::optional<int32_t> getBatteryCapacity(int32_t deviceId, int32_t batteryId) const override { + return std::nullopt; + }; + + /* Query battery status. */ + std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const override { + return std::nullopt; + }; + + void requestReopenDevices() override {} + void wake() override {} + void dump(std::string& dump) const override {} + void monitor() const override {} + bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); } + status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); } + status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); } +}; + +class FuzzPointerController : public PointerControllerInterface { + std::shared_ptr<FuzzedDataProvider> mFdp; + +public: + FuzzPointerController(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {} + ~FuzzPointerController() {} + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override { + return mFdp->ConsumeBool(); + } + void move(float deltaX, float deltaY) override {} + void setButtonState(int32_t buttonState) override {} + int32_t getButtonState() const override { return mFdp->ConsumeIntegral<int32_t>(); } + void setPosition(float x, float y) override {} + void getPosition(float* outX, float* outY) const override {} + void fade(Transition transition) override {} + void unfade(Transition transition) override {} + void setPresentation(Presentation presentation) override {} + void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) override {} + void clearSpots() override {} + int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); } + void setDisplayViewport(const DisplayViewport& displayViewport) override {} +}; + +class FuzzInputReaderPolicy : public InputReaderPolicyInterface { + TouchAffineTransformation mTransform; + std::shared_ptr<FuzzPointerController> mPointerController; + std::shared_ptr<FuzzedDataProvider> mFdp; + +protected: + ~FuzzInputReaderPolicy() {} + +public: + FuzzInputReaderPolicy(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) { + mPointerController = std::make_shared<FuzzPointerController>(mFdp); + } + void getReaderConfiguration(InputReaderConfiguration* outConfig) override {} + std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override { + return mPointerController; + } + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {} + std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier& identifier) override { + return nullptr; + } + std::string getDeviceAlias(const InputDeviceIdentifier& identifier) { + return mFdp->ConsumeRandomLengthString(32); + } + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, + ui::Rotation surfaceRotation) override { + return mTransform; + } + void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; } + void notifyStylusGestureStarted(int32_t, nsecs_t) {} +}; + +class FuzzInputListener : public virtual InputListenerInterface { +public: + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override {} + void notifyKey(const NotifyKeyArgs* args) override {} + void notifyMotion(const NotifyMotionArgs* args) override {} + void notifySwitch(const NotifySwitchArgs* args) override {} + void notifySensor(const NotifySensorArgs* args) override{}; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override{}; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override {} + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override{}; +}; + +class FuzzInputReaderContext : public InputReaderContext { + std::shared_ptr<EventHubInterface> mEventHub; + sp<InputReaderPolicyInterface> mPolicy; + std::shared_ptr<FuzzedDataProvider> mFdp; + +public: + FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, + InputListenerInterface& listener, + std::shared_ptr<FuzzedDataProvider> mFdp) + : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {} + ~FuzzInputReaderContext() {} + void updateGlobalMetaState() override {} + int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); } + void disableVirtualKeysUntil(nsecs_t time) override {} + bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override { + return mFdp->ConsumeBool(); + } + void fadePointer() override {} + std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override { + return mPolicy->obtainPointerController(0); + } + void requestTimeoutAtTime(nsecs_t when) override {} + int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); } + void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {} + std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState) override { + return {}; + } + InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); } + EventHubInterface* getEventHub() override { return mEventHub.get(); } + int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); } + + void updateLedMetaState(int32_t metaState) override{}; + int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); }; + void notifyStylusGestureStarted(int32_t, nsecs_t) {} +}; + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp new file mode 100644 index 0000000000..99fd0831a9 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2022 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 <FuzzContainer.h> +#include <MultiTouchInputMapper.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +const int32_t kMaxKeycodes = 100; + +static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) { + // Pick a random property to set for the mapper to have set. + fdp->PickValueInArray<std::function<void()>>( + {[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); }, + [&]() -> void { + fuzzer.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.size.isSummed", + fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.size.calibration", + fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.pressure.scale", + fdp->ConsumeRandomLengthString(8).data()); + }, + [&]() -> void { + fuzzer.addProperty("touch.size.calibration", + fdp->ConsumeBool() ? "diameter" : "area"); + }, + [&]() -> void { + fuzzer.addProperty("touch.pressure.calibration", + fdp->ConsumeRandomLengthString(8).data()); + }})(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size); + FuzzContainer fuzzer(fdp); + + MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(); + auto policyConfig = fuzzer.getPolicyConfig(); + + // Loop through mapper operations until randomness is exhausted. + while (fdp->remaining_bytes() > 0) { + fdp->PickValueInArray<std::function<void()>>({ + [&]() -> void { addProperty(fuzzer, fdp); }, + [&]() -> void { + std::string dump; + mapper.dump(dump); + }, + [&]() -> void { + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + }, + [&]() -> void { mapper.getSources(); }, + [&]() -> void { + std::list<NotifyArgs> unused = + mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, + fdp->ConsumeIntegral<uint32_t>()); + }, + [&]() -> void { + std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); + }, + [&]() -> void { + int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) + : fdp->ConsumeIntegral<int32_t>(); + int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) + : fdp->ConsumeIntegral<int32_t>(); + RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<int32_t>(), + type, + code, + fdp->ConsumeIntegral<int32_t>()}; + std::list<NotifyArgs> unused = mapper.process(&rawEvent); + }, + [&]() -> void { + mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + [&]() -> void { + std::vector<int32_t> keyCodes; + int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes); + for (int32_t i = 0; i < numBytes; ++i) { + keyCodes.push_back(fdp->ConsumeIntegral<int32_t>()); + } + mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes, + nullptr); + }, + [&]() -> void { + std::list<NotifyArgs> unused = + mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<nsecs_t>()); + }, + [&]() -> void { + std::list<NotifyArgs> unused = + mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>()); + }, + [&]() -> void { + StylusState state{fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()}; + std::list<NotifyArgs> unused = mapper.updateExternalStylusState(state); + }, + [&]() -> void { mapper.getAssociatedDisplayId(); }, + })(); + } + + return 0; +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp new file mode 100644 index 0000000000..7416ce9d47 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 <FuzzContainer.h> +#include <SwitchInputMapper.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size); + FuzzContainer fuzzer(fdp); + + SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(); + auto policyConfig = fuzzer.getPolicyConfig(); + + // Loop through mapper operations until randomness is exhausted. + while (fdp->remaining_bytes() > 0) { + fdp->PickValueInArray<std::function<void()>>({ + [&]() -> void { + std::string dump; + mapper.dump(dump); + }, + [&]() -> void { mapper.getSources(); }, + [&]() -> void { + int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) + : fdp->ConsumeIntegral<int32_t>(); + int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) + : fdp->ConsumeIntegral<int32_t>(); + RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<nsecs_t>(), + fdp->ConsumeIntegral<int32_t>(), + type, + code, + fdp->ConsumeIntegral<int32_t>()}; + std::list<NotifyArgs> unused = mapper.process(&rawEvent); + }, + [&]() -> void { + mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(), + fdp->ConsumeIntegral<int32_t>()); + }, + })(); + } + + return 0; +} + +} // namespace android diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 6fbba3f568..7fb33e5dcf 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -38,7 +38,9 @@ cc_library_shared { "libutils", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-V3-cpp", + "android.hardware.power@1.2", + "android.hardware.power@1.3", + "android.hardware.power-V4-cpp", ], cflags: [ diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index 8c225d5d02..f89035fd1c 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -33,16 +33,20 @@ namespace power { // ------------------------------------------------------------------------------------------------- std::unique_ptr<HalWrapper> HalConnector::connect() { - sp<IPower> halAidl = PowerHalLoader::loadAidl(); - if (halAidl) { + if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) { return std::make_unique<AidlHalWrapper>(halAidl); } - sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0(); - sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1(); - if (halHidlV1_1) { - return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1); - } - if (halHidlV1_0) { + // If V1_0 isn't defined, none of them are + if (sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0()) { + if (sp<V1_3::IPower> halHidlV1_3 = PowerHalLoader::loadHidlV1_3()) { + return std::make_unique<HidlHalWrapperV1_3>(halHidlV1_3); + } + if (sp<V1_2::IPower> halHidlV1_2 = PowerHalLoader::loadHidlV1_2()) { + return std::make_unique<HidlHalWrapperV1_2>(halHidlV1_2); + } + if (sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1()) { + return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_1); + } return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0); } return nullptr; diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp index 1f1b43a607..6bd40f8ff2 100644 --- a/services/powermanager/PowerHalLoader.cpp +++ b/services/powermanager/PowerHalLoader.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "PowerHalLoader" #include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/1.2/IPower.h> +#include <android/hardware/power/1.3/IPower.h> #include <android/hardware/power/IPower.h> #include <binder/IServiceManager.h> #include <hardware/power.h> @@ -55,12 +57,16 @@ std::mutex PowerHalLoader::gHalMutex; sp<IPower> PowerHalLoader::gHalAidl = nullptr; sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr; sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr; +sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr; +sp<V1_3::IPower> PowerHalLoader::gHalHidlV1_3 = nullptr; void PowerHalLoader::unloadAll() { std::lock_guard<std::mutex> lock(gHalMutex); gHalAidl = nullptr; gHalHidlV1_0 = nullptr; gHalHidlV1_1 = nullptr; + gHalHidlV1_2 = nullptr; + gHalHidlV1_3 = nullptr; } sp<IPower> PowerHalLoader::loadAidl() { @@ -82,6 +88,20 @@ sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() { return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1"); } +sp<V1_2::IPower> PowerHalLoader::loadHidlV1_2() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return V1_2::IPower::castFrom(loadHidlV1_0Locked()); }; + return loadHal<V1_2::IPower>(gHalExists, gHalHidlV1_2, loadFn, "HIDL v1.2"); +} + +sp<V1_3::IPower> PowerHalLoader::loadHidlV1_3() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return V1_3::IPower::castFrom(loadHidlV1_0Locked()); }; + return loadHal<V1_3::IPower>(gHalExists, gHalHidlV1_3, loadFn, "HIDL v1.3"); +} + sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() { static bool gHalExists = true; static auto loadFn = []() { return V1_0::IPower::getService(); }; diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index d74bd23a8d..9e7adf8e5c 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -24,8 +24,6 @@ #include <cinttypes> using namespace android::hardware::power; -namespace V1_0 = android::hardware::power::V1_0; -namespace V1_1 = android::hardware::power::V1_1; namespace Aidl = android::hardware::power; namespace android { @@ -108,7 +106,7 @@ HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() { HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) { if (boost == Boost::INTERACTION) { - return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs); + return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs); } else { ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); return HalResult<void>::unsupported(); @@ -119,13 +117,13 @@ HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) { uint32_t data = enabled ? 1 : 0; switch (mode) { case Mode::LAUNCH: - return sendPowerHint(V1_0::PowerHint::LAUNCH, data); + return sendPowerHint(V1_3::PowerHint::LAUNCH, data); case Mode::LOW_POWER: - return sendPowerHint(V1_0::PowerHint::LOW_POWER, data); + return sendPowerHint(V1_3::PowerHint::LOW_POWER, data); case Mode::SUSTAINED_PERFORMANCE: - return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data); + return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data); case Mode::VR: - return sendPowerHint(V1_0::PowerHint::VR_MODE, data); + return sendPowerHint(V1_3::PowerHint::VR_MODE, data); case Mode::INTERACTIVE: return setInteractive(enabled); case Mode::DOUBLE_TAP_TO_WAKE: @@ -137,8 +135,8 @@ HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) { } } -HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { - auto ret = mHandleV1_0->powerHint(hintId, data); +HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) { + auto ret = mHandleV1_0->powerHint(static_cast<V1_0::PowerHint>(hintId), data); return HalResult<void>::fromReturn(ret); } @@ -152,7 +150,7 @@ HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabl return HalResult<void>::fromReturn(ret); } -HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( +HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", threadIds.size()); @@ -166,8 +164,59 @@ HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() { // ------------------------------------------------------------------------------------------------- -HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { - auto ret = mHandleV1_1->powerHintAsync(hintId, data); +HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) { + auto handle = static_cast<V1_1::IPower*>(mHandleV1_0.get()); + auto ret = handle->powerHintAsync(static_cast<V1_0::PowerHint>(hintId), data); + return HalResult<void>::fromReturn(ret); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HidlHalWrapperV1_2::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) { + auto handle = static_cast<V1_2::IPower*>(mHandleV1_0.get()); + auto ret = handle->powerHintAsync_1_2(static_cast<V1_2::PowerHint>(hintId), data); + return HalResult<void>::fromReturn(ret); +} + +HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) { + switch (boost) { + case Boost::CAMERA_SHOT: + return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs); + case Boost::CAMERA_LAUNCH: + return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs); + default: + return HidlHalWrapperV1_1::setBoost(boost, durationMs); + } +} + +HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) { + uint32_t data = enabled ? 1 : 0; + switch (mode) { + case Mode::CAMERA_STREAMING_SECURE: + case Mode::CAMERA_STREAMING_LOW: + case Mode::CAMERA_STREAMING_MID: + case Mode::CAMERA_STREAMING_HIGH: + return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data); + case Mode::AUDIO_STREAMING_LOW_LATENCY: + return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data); + default: + return HidlHalWrapperV1_1::setMode(mode, enabled); + } +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) { + uint32_t data = enabled ? 1 : 0; + if (mode == Mode::EXPENSIVE_RENDERING) { + return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data); + } + return HidlHalWrapperV1_2::setMode(mode, enabled); +} + +HalResult<void> HidlHalWrapperV1_3::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) { + auto handle = static_cast<V1_3::IPower*>(mHandleV1_0.get()); + auto ret = handle->powerHintAsync_1_3(hintId, data); return HalResult<void>::fromReturn(ret); } diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp index fcb012fc75..4343aec227 100644 --- a/services/powermanager/benchmarks/Android.bp +++ b/services/powermanager/benchmarks/Android.bp @@ -38,7 +38,9 @@ cc_benchmark { "libutils", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-V3-cpp", + "android.hardware.power@1.2", + "android.hardware.power@1.3", + "android.hardware.power-V4-cpp", ], static_libs: [ "libtestUtil", diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp index 962784cbae..54dffcf817 100644 --- a/services/powermanager/tests/Android.bp +++ b/services/powermanager/tests/Android.bp @@ -31,6 +31,8 @@ cc_test { "PowerHalWrapperAidlTest.cpp", "PowerHalWrapperHidlV1_0Test.cpp", "PowerHalWrapperHidlV1_1Test.cpp", + "PowerHalWrapperHidlV1_2Test.cpp", + "PowerHalWrapperHidlV1_3Test.cpp", "WorkSourceTest.cpp", ], cflags: [ @@ -47,7 +49,9 @@ cc_test { "libutils", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-V3-cpp", + "android.hardware.power@1.2", + "android.hardware.power@1.3", + "android.hardware.power-V4-cpp", ], static_libs: [ "libgmock", diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp index 058e1b5ca8..e36deed042 100644 --- a/services/powermanager/tests/PowerHalLoaderTest.cpp +++ b/services/powermanager/tests/PowerHalLoaderTest.cpp @@ -26,6 +26,8 @@ using IPowerV1_0 = android::hardware::power::V1_0::IPower; using IPowerV1_1 = android::hardware::power::V1_1::IPower; +using IPowerV1_2 = android::hardware::power::V1_2::IPower; +using IPowerV1_3 = android::hardware::power::V1_3::IPower; using IPowerAidl = android::hardware::power::IPower; using namespace android; @@ -52,6 +54,16 @@ sp<IPowerV1_1> loadHal<IPowerV1_1>() { return PowerHalLoader::loadHidlV1_1(); } +template <> +sp<IPowerV1_2> loadHal<IPowerV1_2>() { + return PowerHalLoader::loadHidlV1_2(); +} + +template <> +sp<IPowerV1_3> loadHal<IPowerV1_3>() { + return PowerHalLoader::loadHidlV1_3(); +} + // ------------------------------------------------------------------------------------------------- template <typename T> @@ -63,7 +75,7 @@ public: // ------------------------------------------------------------------------------------------------- -typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes; +typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes; TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes); TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) { diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp index b54762cf6d..0cd2e22e7e 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp @@ -87,18 +87,28 @@ TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) { } TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::ML_ACC, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10); + ASSERT_TRUE(result.isUnsupported()); } TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1)); - EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1)); - EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(false))) .Times(Exactly(1)); - EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) @@ -131,6 +141,16 @@ TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) { } TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) { + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::GAME_LOADING, false); + ASSERT_TRUE(result.isUnsupported()); } diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp index d30e8d2c49..32f84e20b6 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp @@ -31,7 +31,6 @@ using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::PowerHint; using IPowerV1_1 = android::hardware::power::V1_1::IPower; -using IPowerV1_0 = android::hardware::power::V1_0::IPower; using namespace android; using namespace android::power; @@ -40,15 +39,6 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- -class MockIPowerV1_0 : public IPowerV1_0 { -public: - MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); - MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); - MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); - MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, - (getPlatformLowPowerStats_cb _hidl_cb), (override)); -}; - class MockIPowerV1_1 : public IPowerV1_1 { public: MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); @@ -69,23 +59,22 @@ public: protected: std::unique_ptr<HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr; - sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr; + sp<StrictMock<MockIPowerV1_1>> mMockHal = nullptr; }; // ------------------------------------------------------------------------------------------------- void PowerHalWrapperHidlV1_1Test::SetUp() { - mMockHalV1_0 = new StrictMock<MockIPowerV1_0>(); - mMockHalV1_1 = new StrictMock<MockIPowerV1_1>(); - mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1); + mMockHal = new StrictMock<MockIPowerV1_1>(); + mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHal); ASSERT_NE(mWrapper, nullptr); + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0); } // ------------------------------------------------------------------------------------------------- TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) { - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) .Times(Exactly(1)); auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); @@ -93,7 +82,7 @@ TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) { } TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) { - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) .Times(Exactly(1)) .WillRepeatedly([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); @@ -104,24 +93,31 @@ TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) { } TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::ML_ACC, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10); + ASSERT_TRUE(result.isUnsupported()); } TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) { { InSequence seq; - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true))) .Times(Exactly(1)); - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(false))) .Times(Exactly(1)); - EXPECT_CALL(*mMockHalV1_1.get(), - powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true))) .Times(Exactly(1)); - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(false))) .Times(Exactly(1)); - EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1)); - EXPECT_CALL(*mMockHalV1_0.get(), + EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) .Times(Exactly(1)); } @@ -141,7 +137,7 @@ TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) { } TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) { - EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true))) .Times(Exactly(1)) .WillRepeatedly([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); @@ -152,6 +148,16 @@ TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) { } TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::GAME_LOADING, false); + ASSERT_TRUE(result.isUnsupported()); } diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp new file mode 100644 index 0000000000..cf48409f5f --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_2Test" + +#include <android/hardware/power/1.2/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint; +using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint; + +using IPowerV1_2 = android::hardware::power::V1_2::IPower; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_2 : public IPowerV1_2 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data), + (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data), + (override)); + MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats, + (getSubsystemLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_2Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_2>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_2Test::SetUp() { + mMockHal = new StrictMock<MockIPowerV1_2>(); + mWrapper = std::make_unique<HidlHalWrapperV1_2>(mMockHal); + ASSERT_NE(mWrapper, nullptr); + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_SHOT), Eq(500))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_LAUNCH), Eq(300))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300); + ASSERT_TRUE(result.isOk()); +} + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHintV1_2, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + + auto result = mWrapper->setBoost(Boost::ML_ACC, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10); + ASSERT_TRUE(result.isUnsupported()); +} + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetMode) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LOW_POWER), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_2(Eq(PowerHintV1_2::SUSTAINED_PERFORMANCE), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::VR_MODE), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true)); + EXPECT_CALL(*mMockHal.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(true))) + .Times(Exactly(2)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(false))) + .Times(Exactly(2)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_2(Eq(PowerHintV1_2::AUDIO_LOW_LATENCY), Eq(true))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true); + ASSERT_TRUE(result.isOk()); +} + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHintV1_2, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeIgnored) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + + auto result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::GAME_LOADING, false); + ASSERT_TRUE(result.isUnsupported()); +} diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp new file mode 100644 index 0000000000..2c48537edc --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_3Test" + +#include <android/hardware/power/1.3/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint; +using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint; +using PowerHintV1_3 = android::hardware::power::V1_3::PowerHint; + +using IPowerV1_3 = android::hardware::power::V1_3::IPower; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_3 : public IPowerV1_3 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data), + (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data), + (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_3, (PowerHintV1_3 hint, int32_t data), + (override)); + + MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats, + (getSubsystemLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_3Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_3>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_3Test::SetUp() { + mMockHal = new StrictMock<MockIPowerV1_3>(); + mWrapper = std::make_unique<HidlHalWrapperV1_3>(mMockHal); + ASSERT_NE(mWrapper, nullptr); + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_SHOT), Eq(500))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_LAUNCH), Eq(300))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300); + ASSERT_TRUE(result.isOk()); +} + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHintV1_3, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + + auto result = mWrapper->setBoost(Boost::ML_ACC, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10); + ASSERT_TRUE(result.isUnsupported()); +} + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetMode) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LOW_POWER), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_3(Eq(PowerHintV1_3::SUSTAINED_PERFORMANCE), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::VR_MODE), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true)); + EXPECT_CALL(*mMockHal.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(true))) + .Times(Exactly(2)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(false))) + .Times(Exactly(2)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_3(Eq(PowerHintV1_3::AUDIO_LOW_LATENCY), Eq(true))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + powerHintAsync_1_3(Eq(PowerHintV1_3::EXPENSIVE_RENDERING), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true); + ASSERT_TRUE(result.isOk()); + result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false); + ASSERT_TRUE(result.isOk()); +} + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHintV1_3, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeIgnored) { + EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0); + EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0); + EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0); + + auto result = mWrapper->setMode(Mode::GAME, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->setMode(Mode::GAME_LOADING, false); + ASSERT_TRUE(result.isUnsupported()); +} diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index a2042d6451..0eeb820b87 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -7,7 +7,7 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library { name: "libsensorservice", srcs: [ @@ -91,6 +91,12 @@ cc_library_shared { afdo: true, } +cc_library_headers { + name: "libsensorservice_headers", + export_include_dirs: ["."], + visibility: ["//frameworks/native/services/sensorservice/fuzzer"], +} + cc_binary { name: "sensorservice", diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index de050e02d0..10ca990f87 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -39,7 +39,6 @@ #include <thread> using namespace android::hardware::sensors; -using android::hardware::Return; using android::util::ProtoOutputStream; namespace android { diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 2dd12e9446..291c770692 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "SensorDevice.h" #include "SensorDirectConnection.h" #include <android/util/ProtoOutputStream.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <hardware/sensors.h> +#include "SensorDevice.h" #define UNUSED(x) (void)(x) @@ -51,7 +51,7 @@ void SensorService::SensorDirectConnection::destroy() { stopAll(); mService->cleanupConnection(this); if (mMem.handle != nullptr) { - native_handle_close(mMem.handle); + native_handle_close_with_tag(mMem.handle); native_handle_delete(const_cast<struct native_handle*>(mMem.handle)); } mDestroyed = true; diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp index 46f00e8329..398cdf9a0e 100644 --- a/services/sensorservice/SensorInterface.cpp +++ b/services/sensorservice/SensorInterface.cpp @@ -87,6 +87,42 @@ VirtualSensor::VirtualSensor() : // --------------------------------------------------------------------------- +RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback) + : BaseSensor(sensor), mCallback(std::move(callback)) { +} + +status_t RuntimeSensor::activate(void*, bool enabled) { + if (enabled != mEnabled) { + mEnabled = enabled; + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + return OK; +} + +status_t RuntimeSensor::batch(void*, int, int, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { + if (mSamplingPeriodNs != samplingPeriodNs || mBatchReportLatencyNs != maxBatchReportLatencyNs) { + mSamplingPeriodNs = samplingPeriodNs; + mBatchReportLatencyNs = maxBatchReportLatencyNs; + if (mEnabled) { + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + } + return OK; +} + +status_t RuntimeSensor::setDelay(void*, int, int64_t ns) { + if (mSamplingPeriodNs != ns) { + mSamplingPeriodNs = ns; + if (mEnabled) { + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + } + return OK; +} + +// --------------------------------------------------------------------------- + ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service) : HardwareSensor(sensor), mSensorService(service) { } diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h index 57043592c5..5ee5e1224a 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -104,6 +104,32 @@ protected: // --------------------------------------------------------------------------- +class RuntimeSensor : public BaseSensor { +public: + static constexpr int DEFAULT_DEVICE_ID = 0; + + class StateChangeCallback : public virtual RefBase { + public: + virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs, + int64_t batchReportLatencyNs) = 0; + }; + RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback); + virtual status_t activate(void* ident, bool enabled) override; + virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) override; + virtual status_t setDelay(void* ident, int handle, int64_t ns) override; + virtual bool process(sensors_event_t*, const sensors_event_t&) { return false; } + virtual bool isVirtual() const override { return false; } + +private: + bool mEnabled = false; + int64_t mSamplingPeriodNs = 0; + int64_t mBatchReportLatencyNs = 0; + sp<StateChangeCallback> mCallback; +}; + +// --------------------------------------------------------------------------- + class ProximitySensor : public HardwareSensor { public: explicit ProximitySensor(const sensor_t& sensor, SensorService& service); diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp index 85ce0f0018..6d36b4789b 100644 --- a/services/sensorservice/SensorList.cpp +++ b/services/sensorservice/SensorList.cpp @@ -29,12 +29,12 @@ namespace SensorServiceUtil { const Sensor SensorList::mNonSensor = Sensor("unknown"); bool SensorList::add( - int handle, SensorInterface* si, bool isForDebug, bool isVirtual) { + int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) { std::lock_guard<std::mutex> lk(mLock); if (handle == si->getSensor().getHandle() && mUsedHandle.insert(handle).second) { // will succeed as the mUsedHandle does not have this handle - mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual)); + mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId)); return true; } // handle exist already or handle mismatch @@ -79,7 +79,8 @@ const Vector<Sensor> SensorList::getUserSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) { + if (!e.isForDebug && !e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -92,7 +93,8 @@ const Vector<Sensor> SensorList::getUserDebugSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.si->getSensor().isDynamicSensor()) { + if (!e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -105,7 +107,8 @@ const Vector<Sensor> SensorList::getDynamicSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) { + if (!e.isForDebug && e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -118,7 +121,20 @@ const Vector<Sensor> SensorList::getVirtualSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (e.isVirtual) { + if (e.isVirtual && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { + sensors.add(e.si->getSensor()); + } + return true; + }); + return sensors; +} + +const Vector<Sensor> SensorList::getRuntimeSensors(int deviceId) const { + // lock in forEachEntry + Vector<Sensor> sensors; + forEachEntry( + [&sensors, deviceId] (const Entry& e) -> bool { + if (!e.isForDebug && e.deviceId == deviceId) { sensors.add(e.si->getSensor()); } return true; diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h index 049ae7c855..79f6701922 100644 --- a/services/sensorservice/SensorList.h +++ b/services/sensorservice/SensorList.h @@ -40,14 +40,16 @@ public: sp<SensorInterface> si; const bool isForDebug; const bool isVirtual; - Entry(SensorInterface* si_, bool debug_, bool virtual_) : - si(si_), isForDebug(debug_), isVirtual(virtual_) { + const int deviceId; + Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) : + si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) { } }; // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the // object it pointed to and the object should not be released elsewhere. - bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false); + bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false, + int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID); // After a handle is removed, the object that SensorInterface * pointing to may get deleted if // no more sp<> of the same object exist. @@ -60,6 +62,7 @@ public: const Vector<Sensor> getUserDebugSensors() const; const Vector<Sensor> getDynamicSensors() const; const Vector<Sensor> getVirtualSensors() const; + const Vector<Sensor> getRuntimeSensors(int deviceId) const; String8 getName(int handle) const; String8 getStringType(int handle) const; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index e0a4f034cb..0c9fef5652 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -16,7 +16,6 @@ #include <android-base/strings.h> #include <android/content/pm/IPackageManagerNative.h> #include <android/util/ProtoOutputStream.h> -#include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <binder/ActivityManager.h> #include <binder/BinderService.h> #include <binder/IServiceManager.h> @@ -25,6 +24,7 @@ #include <cutils/ashmem.h> #include <cutils/misc.h> #include <cutils/properties.h> +#include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <hardware/sensors.h> #include <hardware_legacy/power.h> #include <log/log.h> @@ -102,6 +102,33 @@ static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE"); static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS"); +namespace { + +// TODO(b/259227294): Move the sensor ranges to the HAL. +int32_t nextRuntimeSensorHandle() { + static constexpr int32_t kRuntimeHandleBase = 0x5F000000; + static constexpr int32_t kRuntimeHandleEnd = 0x5FFFFFFF; + static int32_t nextHandle = kRuntimeHandleBase; + if (nextHandle == kRuntimeHandleEnd) { + return -1; + } + return nextHandle++; +} + +class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback { + public: + RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback) + : mCallback(std::move(callback)) {} + void onStateChanged(bool enabled, int64_t samplingPeriodNs, + int64_t batchReportLatencyNs) override { + mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs); + } + private: + sp<SensorService::RuntimeSensorStateChangeCallback> mCallback; +}; + +} // namespace + static bool isAutomotive() { sp<IServiceManager> serviceManager = defaultServiceManager(); if (serviceManager.get() == nullptr) { @@ -137,6 +164,60 @@ SensorService::SensorService() mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this); } +int SensorService::registerRuntimeSensor( + const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) { + int handle = 0; + while (handle == 0 || !mSensors.isNewHandle(handle)) { + handle = nextRuntimeSensorHandle(); + if (handle < 0) { + // Ran out of the dedicated range for runtime sensors. + return handle; + } + } + + ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s", + handle, sensor.type, sensor.name); + + sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback( + new RuntimeSensorCallbackProxy(std::move(callback))); + sensor_t runtimeSensor = sensor; + // force the handle to be consistent + runtimeSensor.handle = handle; + SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback)); + + Mutex::Autolock _l(mLock); + const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId); + + if (s.getHandle() != handle) { + // The registration was unsuccessful. + return s.getHandle(); + } + return handle; +} + +status_t SensorService::unregisterRuntimeSensor(int handle) { + ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle); + { + Mutex::Autolock _l(mLock); + if (!unregisterDynamicSensorLocked(handle)) { + ALOGE("Runtime sensor release error."); + return UNKNOWN_ERROR; + } + } + + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) { + connection->removeSensor(handle); + } + return OK; +} + +status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) { + Mutex::Autolock _l(mLock); + mRuntimeSensorEventQueue.push(event); + return OK; +} + bool SensorService::initializeHmacKey() { int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC); if (fd != -1) { @@ -407,10 +488,11 @@ bool SensorService::hasSensorAccessLocked(uid_t uid, const String16& opPackageNa && isUidActive(uid) && !isOperationRestrictedLocked(opPackageName); } -const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) { +const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual, + int deviceId) { int handle = s->getSensor().getHandle(); int type = s->getSensor().getType(); - if (mSensors.add(handle, s, isDebug, isVirtual)){ + if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) { mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type)); return s->getSensor(); } else { @@ -1003,6 +1085,7 @@ bool SensorService::threadLoop() { recordLastValueLocked(mSensorEventBuffer, count); // handle virtual sensors + bool bufferNeedsSorting = false; if (count && vcount) { sensors_event_t const * const event = mSensorEventBuffer; if (!mActiveVirtualSensors.empty()) { @@ -1038,12 +1121,37 @@ bool SensorService::threadLoop() { // record the last synthesized values recordLastValueLocked(&mSensorEventBuffer[count], k); count += k; - // sort the buffer by time-stamps - sortEventBuffer(mSensorEventBuffer, count); + bufferNeedsSorting = true; } } } + // handle runtime sensors + { + size_t k = 0; + while (!mRuntimeSensorEventQueue.empty()) { + if (count + k >= minBufferSize) { + ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu", + count, k, minBufferSize); + break; + } + mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front(); + mRuntimeSensorEventQueue.pop(); + k++; + } + if (k) { + // record the last synthesized values + recordLastValueLocked(&mSensorEventBuffer[count], k); + count += k; + bufferNeedsSorting = true; + } + } + + if (bufferNeedsSorting) { + // sort the buffer by time-stamps + sortEventBuffer(mSensorEventBuffer, count); + } + // handle backward compatibility for RotationVector sensor if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) { for (int i = 0; i < count; i++) { @@ -1342,19 +1450,37 @@ Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) { return accessibleSensorList; } +void SensorService::addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor, + Vector<Sensor>& accessibleSensorList) { + if (canAccessSensor(sensor, "can't see", opPackageName)) { + accessibleSensorList.add(sensor); + } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) { + ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32, + sensor.getName().string(), sensor.getRequiredPermission().string(), + sensor.getRequiredAppOp()); + } +} + Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) { Vector<Sensor> accessibleSensorList; mSensors.forEachSensor( [this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool { if (sensor.isDynamicSensor()) { - if (canAccessSensor(sensor, "can't see", opPackageName)) { - accessibleSensorList.add(sensor); - } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) { - ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32, - sensor.getName().string(), - sensor.getRequiredPermission().string(), - sensor.getRequiredAppOp()); - } + addSensorIfAccessible(opPackageName, sensor, accessibleSensorList); + } + return true; + }); + makeUuidsIntoIdsForSensorList(accessibleSensorList); + return accessibleSensorList; +} + +Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName, int deviceId) { + Vector<Sensor> accessibleSensorList; + mSensors.forEachEntry( + [this, &opPackageName, deviceId, &accessibleSensorList] ( + const SensorServiceUtil::SensorList::Entry& e) -> bool { + if (e.deviceId == deviceId) { + addSensorIfAccessible(opPackageName, e.si->getSensor(), accessibleSensorList); } return true; }); @@ -1475,6 +1601,7 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( if (!clone) { return nullptr; } + native_handle_set_fdsan_tag(clone); sp<SensorDirectConnection> conn; SensorDevice& dev(SensorDevice::getInstance()); @@ -1488,7 +1615,7 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( } if (conn == nullptr) { - native_handle_close(clone); + native_handle_close_with_tag(clone); native_handle_delete(clone); } else { // add to list of direct connections diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 4ba3c51985..e4903987bc 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -42,6 +42,7 @@ #include <stdint.h> #include <sys/types.h> +#include <queue> #include <unordered_map> #include <unordered_set> #include <vector> @@ -143,6 +144,14 @@ public: virtual void onProximityActive(bool isActive) = 0; }; + class RuntimeSensorStateChangeCallback : public virtual RefBase { + public: + // Note that the callback is invoked from an async thread and can interact with the + // SensorService directly. + virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos, + int64_t batchReportLatencyNanos) = 0; + }; + static char const* getServiceName() ANDROID_API { return "sensorservice"; } SensorService() ANDROID_API; @@ -169,6 +178,11 @@ public: status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API; status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API; + int registerRuntimeSensor(const sensor_t& sensor, int deviceId, + sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API; + status_t unregisterRuntimeSensor(int handle) ANDROID_API; + status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API; + // Returns true if a sensor should be throttled according to our rate-throttling rules. static bool isSensorInCappedSet(int sensorType); @@ -346,6 +360,7 @@ private: // ISensorServer interface virtual Vector<Sensor> getSensorList(const String16& opPackageName); virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName); + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId); virtual sp<ISensorEventConnection> createSensorEventConnection( const String8& packageName, int requestedMode, const String16& opPackageName, const String16& attributionTag); @@ -364,8 +379,9 @@ private: bool isWakeUpSensor(int type) const; void recordLastValueLocked(sensors_event_t const* buffer, size_t count); static void sortEventBuffer(sensors_event_t* buffer, size_t count); - const Sensor& registerSensor(SensorInterface* sensor, - bool isDebug = false, bool isVirtual = false); + const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false, + bool isVirtual = false, + int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID); const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false); const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false); bool unregisterDynamicSensorLocked(int handle); @@ -375,6 +391,8 @@ private: sensors_event_t const* buffer, const int count); bool canAccessSensor(const Sensor& sensor, const char* operation, const String16& opPackageName); + void addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor, + Vector<Sensor>& accessibleSensorList); static bool hasPermissionForSensor(const Sensor& sensor); static int getTargetSdkVersion(const String16& opPackageName); static void resetTargetSdkVersionCache(const String16& opPackageName); @@ -492,6 +510,7 @@ private: wp<const SensorEventConnection> * mMapFlushEventsToConnections; std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; Mode mCurrentOperatingMode; + std::queue<sensors_event_t> mRuntimeSensorEventQueue; // true if the head tracker sensor type is currently restricted to system usage only // (can only be unrestricted for testing, via shell cmd) diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index caf7f03361..b00d1a761b 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -89,6 +89,17 @@ void testInvalidSharedMem_NoCrash(SensorManager &mgr) { // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart printf("createInvalidDirectChannel=%d\n", ret); + + // Secondary test: correct channel creation & destruction (should print 0) + ret = mgr.createDirectChannel(kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER, + resourceHandle); + printf("createValidDirectChannel=%d\n", ret); + + // Third test: double-destroy (should not crash) + mgr.destroyDirectChannel(ret); + AHardwareBuffer_release(hardwareBuffer); + printf("duplicate destroyDirectChannel...\n"); + mgr.destroyDirectChannel(ret); } int main() { diff --git a/services/stats/Android.bp b/services/stats/Android.bp index 7fea6161ff..7d358e1bd2 100644 --- a/services/stats/Android.bp +++ b/services/stats/Android.bp @@ -13,10 +13,13 @@ cc_library_shared { "StatsAidl.cpp", "StatsHal.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "android.frameworks.stats@1.0", - "android.frameworks.stats-V1-ndk", + "android.frameworks.stats-V2-ndk", "libbinder_ndk", "libhidlbase", "liblog", @@ -29,10 +32,12 @@ cc_library_shared { ], export_shared_lib_headers: [ "android.frameworks.stats@1.0", - "android.frameworks.stats-V1-ndk", + "android.frameworks.stats-V2-ndk", ], local_include_dirs: [ "include/stats", ], - vintf_fragments: ["android.frameworks.stats@1.0-service.xml"] + vintf_fragments: [ + "android.frameworks.stats-service.xml", + ], } diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp index a3b68f1dab..9e13849485 100644 --- a/services/stats/StatsAidl.cpp +++ b/services/stats/StatsAidl.cpp @@ -62,6 +62,90 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { AStatsEvent_writeString(event, atomValue.get<VendorAtomValue::stringValue>().c_str()); break; + case VendorAtomValue::boolValue: + AStatsEvent_writeBool(event, + atomValue.get<VendorAtomValue::boolValue>()); + break; + case VendorAtomValue::repeatedIntValue: { + const std::optional<std::vector<int>>& repeatedIntValue = + atomValue.get<VendorAtomValue::repeatedIntValue>(); + if (!repeatedIntValue) { + AStatsEvent_writeInt32Array(event, {}, 0); + break; + } + AStatsEvent_writeInt32Array(event, repeatedIntValue->data(), + repeatedIntValue->size()); + break; + } + case VendorAtomValue::repeatedLongValue: { + const std::optional<std::vector<int64_t>>& repeatedLongValue = + atomValue.get<VendorAtomValue::repeatedLongValue>(); + if (!repeatedLongValue) { + AStatsEvent_writeInt64Array(event, {}, 0); + break; + } + AStatsEvent_writeInt64Array(event, repeatedLongValue->data(), + repeatedLongValue->size()); + break; + } + case VendorAtomValue::repeatedFloatValue: { + const std::optional<std::vector<float>>& repeatedFloatValue = + atomValue.get<VendorAtomValue::repeatedFloatValue>(); + if (!repeatedFloatValue) { + AStatsEvent_writeFloatArray(event, {}, 0); + break; + } + AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(), + repeatedFloatValue->size()); + break; + } + case VendorAtomValue::repeatedStringValue: { + const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue = + atomValue.get<VendorAtomValue::repeatedStringValue>(); + if (!repeatedStringValue) { + AStatsEvent_writeStringArray(event, {}, 0); + break; + } + const std::vector<std::optional<std::string>>& repeatedStringVector = + *repeatedStringValue; + const char* cStringArray[repeatedStringVector.size()]; + + for (int i = 0; i < repeatedStringVector.size(); ++i) { + cStringArray[i] = repeatedStringVector[i].has_value() + ? repeatedStringVector[i]->c_str() + : ""; + } + + AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size()); + break; + } + case VendorAtomValue::repeatedBoolValue: { + const std::optional<std::vector<bool>>& repeatedBoolValue = + atomValue.get<VendorAtomValue::repeatedBoolValue>(); + if (!repeatedBoolValue) { + AStatsEvent_writeBoolArray(event, {}, 0); + break; + } + const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue; + bool boolArray[repeatedBoolValue->size()]; + + for (int i = 0; i < repeatedBoolVector.size(); ++i) { + boolArray[i] = repeatedBoolVector[i]; + } + + AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size()); + break; + } + case VendorAtomValue::byteArrayValue: { + const std::optional<std::vector<uint8_t>>& byteArrayValue = + atomValue.get<VendorAtomValue::byteArrayValue>(); + if (!byteArrayValue) { + AStatsEvent_writeByteArray(event, {}, 0); + break; + } + AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size()); + break; + } } } AStatsEvent_build(event); diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats-service.xml index c564b7be53..7e2635e406 100644 --- a/services/stats/android.frameworks.stats@1.0-service.xml +++ b/services/stats/android.frameworks.stats-service.xml @@ -11,7 +11,7 @@ <hal format="aidl"> <name>android.frameworks.stats</name> - <version>1</version> + <version>2</version> <fqname>IStats/default</fqname> </hal> </manifest> diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index cbb95f963c..0a192c5f61 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -18,12 +18,14 @@ cc_defaults { "-Wunused", "-Wunreachable-code", "-Wconversion", + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], } cc_defaults { name: "libsurfaceflinger_defaults", defaults: [ + "android.hardware.graphics.composer3-ndk_shared", "surfaceflinger_defaults", "skia_renderengine_deps", ], @@ -45,10 +47,9 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libbase", "libbinder", "libbinder_ndk", @@ -83,7 +84,6 @@ cc_defaults { "libserviceutils", "libshaders", "libtonemap", - "libtrace_proto", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -105,7 +105,6 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.power@1.3", "libhidlbase", "libtimestats", @@ -141,26 +140,27 @@ filegroup { name: "libsurfaceflinger_sources", srcs: [ "BackgroundExecutor.cpp", - "BufferLayer.cpp", - "BufferLayerConsumer.cpp", - "BufferQueueLayer.cpp", - "BufferStateLayer.cpp", - "ClientCache.cpp", "Client.cpp", - "EffectLayer.cpp", - "ContainerLayer.cpp", + "ClientCache.cpp", + "Display/DisplaySnapshot.cpp", "DisplayDevice.cpp", "DisplayHardware/AidlComposerHal.cpp", - "DisplayHardware/HidlComposerHal.cpp", "DisplayHardware/ComposerHal.cpp", "DisplayHardware/FramebufferSurface.cpp", "DisplayHardware/HWC2.cpp", "DisplayHardware/HWComposer.cpp", + "DisplayHardware/HidlComposerHal.cpp", "DisplayHardware/PowerAdvisor.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", "EventLog/EventLog.cpp", + "FrontEnd/LayerCreationArgs.cpp", + "FrontEnd/LayerHandle.cpp", + "FrontEnd/LayerHierarchy.cpp", + "FrontEnd/LayerLifecycleManager.cpp", + "FrontEnd/RequestedLayerState.cpp", + "FrontEnd/TransactionHandler.cpp", "FlagManager.cpp", "FpsReporter.cpp", "FrameTracer/FrameTracer.cpp", @@ -168,11 +168,10 @@ filegroup { "HdrLayerInfoReporter.cpp", "WindowInfosListenerInvoker.cpp", "Layer.cpp", + "LayerFE.cpp", "LayerProtoHelper.cpp", - "LayerRejecter.cpp", "LayerRenderArea.cpp", "LayerVector.cpp", - "MonitoredProducer.cpp", "NativeWindowSurface.cpp", "RefreshRateOverlay.cpp", "RegionSamplingThread.cpp", @@ -184,7 +183,7 @@ filegroup { "Scheduler/LayerHistory.cpp", "Scheduler/LayerInfo.cpp", "Scheduler/MessageQueue.cpp", - "Scheduler/RefreshRateConfigs.cpp", + "Scheduler/RefreshRateSelector.cpp", "Scheduler/Scheduler.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncPredictor.cpp", @@ -192,10 +191,10 @@ filegroup { "Scheduler/VsyncConfiguration.cpp", "Scheduler/VsyncModulator.cpp", "Scheduler/VsyncSchedule.cpp", + "ScreenCaptureOutput.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", - "SurfaceInterceptor.cpp", "Tracing/LayerTracing.cpp", "Tracing/TransactionTracing.cpp", "Tracing/TransactionProtoParser.cpp", @@ -230,7 +229,6 @@ cc_defaults { ], static_libs: [ "libserviceutils", - "libtrace_proto", ], } diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp deleted file mode 100644 index d9c89cd821..0000000000 --- a/services/surfaceflinger/BufferLayer.cpp +++ /dev/null @@ -1,814 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -//#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "BufferLayer" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "BufferLayer.h" - -#include <compositionengine/CompositionEngine.h> -#include <compositionengine/LayerFECompositionState.h> -#include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> -#include <cutils/compiler.h> -#include <cutils/native_handle.h> -#include <cutils/properties.h> -#include <gui/BufferItem.h> -#include <gui/BufferQueue.h> -#include <gui/GLConsumer.h> -#include <gui/LayerDebugInfo.h> -#include <gui/Surface.h> -#include <renderengine/RenderEngine.h> -#include <ui/DebugUtils.h> -#include <utils/Errors.h> -#include <utils/Log.h> -#include <utils/NativeHandle.h> -#include <utils/StopWatch.h> -#include <utils/Trace.h> - -#include <cmath> -#include <cstdlib> -#include <mutex> -#include <sstream> - -#include "Colorizer.h" -#include "DisplayDevice.h" -#include "FrameTracer/FrameTracer.h" -#include "LayerRejecter.h" -#include "TimeStats/TimeStats.h" - -namespace android { - -using gui::WindowInfo; - -static constexpr float defaultMaxLuminance = 1000.0; - -BufferLayer::BufferLayer(const LayerCreationArgs& args) - : Layer(args), - mTextureName(args.textureName), - mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} { - ALOGV("Creating Layer %s", getDebugName()); - - mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); - - mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; - mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; -} - -BufferLayer::~BufferLayer() { - if (!isClone()) { - // The original layer and the clone layer share the same texture. Therefore, only one of - // the layers, in this case the original layer, needs to handle the deletion. The original - // layer and the clone should be removed at the same time so there shouldn't be any issue - // with the clone layer trying to use the deleted texture. - mFlinger->deleteTextureAsync(mTextureName); - } - const int32_t layerId = getSequence(); - mFlinger->mTimeStats->onDestroy(layerId); - mFlinger->mFrameTracer->onDestroy(layerId); -} - -void BufferLayer::useSurfaceDamage() { - if (mFlinger->mForceFullDamage) { - surfaceDamageRegion = Region::INVALID_REGION; - } else { - surfaceDamageRegion = mBufferInfo.mSurfaceDamage; - } -} - -void BufferLayer::useEmptyDamage() { - surfaceDamageRegion.clear(); -} - -bool BufferLayer::isOpaque(const Layer::State& s) const { - // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the - // layer's opaque flag. - if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) { - return false; - } - - // if the layer has the opaque flag, then we're always opaque, - // otherwise we use the current buffer's format. - return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat()); -} - -bool BufferLayer::canReceiveInput() const { - return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f); -} - -bool BufferLayer::isVisible() const { - return !isHiddenByPolicy() && getAlpha() > 0.0f && - (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr); -} - -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); - const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - mat4 tr; - - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - tr = tr * rot90; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - tr = tr * flipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - tr = tr * flipV; - } - return inverse(tr); -} - -std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { - ATRACE_CALL(); - - std::optional<compositionengine::LayerFE::LayerSettings> result = - Layer::prepareClientComposition(targetSettings); - if (!result) { - return result; - } - - if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) { - // For surfaceview of tv sideband, there is no activeBuffer - // in bufferqueue, we need return LayerSettings. - return result; - } - const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) || - ((isSecure() || isProtected()) && !targetSettings.isSecure); - const bool bufferCanBeUsedAsHwTexture = - mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE; - compositionengine::LayerFE::LayerSettings& layer = *result; - if (blackOutLayer || !bufferCanBeUsedAsHwTexture) { - ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable", - mName.c_str()); - prepareClearClientComposition(layer, true /* blackout */); - return layer; - } - - const State& s(getDrawingState()); - layer.source.buffer.buffer = mBufferInfo.mBuffer; - layer.source.buffer.isOpaque = isOpaque(s); - layer.source.buffer.fence = mBufferInfo.mFence; - layer.source.buffer.textureName = mTextureName; - layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha(); - layer.source.buffer.isY410BT2020 = isHdrY410(); - bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086; - bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3; - float maxLuminance = 0.f; - if (hasSmpte2086 && hasCta861_3) { - maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance, - mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel); - } else if (hasSmpte2086) { - maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance; - } else if (hasCta861_3) { - maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel; - } else { - switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - case HAL_DATASPACE_TRANSFER_HLG: - // Behavior-match previous releases for HDR content - maxLuminance = defaultMaxLuminance; - break; - } - } - layer.source.buffer.maxLuminanceNits = maxLuminance; - layer.frameNumber = mCurrentFrameNumber; - layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0; - - const bool useFiltering = - targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering(); - - // Query the texture matrix given our current filtering mode. - float textureMatrix[16]; - getDrawingTransformMatrix(useFiltering, textureMatrix); - - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to - * the texture transform - */ - uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags(); - mat4 tr = inverseOrientation(transform); - - /** - * TODO(b/36727915): This is basically a hack. - * - * Ensure that regardless of the parent transformation, - * this buffer is always transformed from native display - * orientation to display orientation. For example, in the case - * of a camera where the buffer remains in native orientation, - * we want the pixels to always be upright. - */ - sp<Layer> p = mDrawingParent.promote(); - if (p != nullptr) { - const auto parentTransform = p->getTransform(); - tr = tr * inverseOrientation(parentTransform.getOrientation()); - } - - // and finally apply it to the original texture matrix - const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr); - memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); - } - - const Rect win{getBounds()}; - 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; - const float translateY = float(win.top) / bufferHeight; - const float translateX = float(win.left) / bufferWidth; - - // Flip y-coordinates because GLConsumer expects OpenGL convention. - mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) * - mat4::translate(vec4(-.5, -.5, 0, 1)) * - mat4::translate(vec4(translateX, translateY, 0, 1)) * - mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0)); - - layer.source.buffer.useTextureFiltering = useFiltering; - layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr; - - return layer; -} - -bool BufferLayer::isHdrY410() const { - // pixel format is HDR Y410 masquerading as RGBA_1010102 - return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ && - mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA && - mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102); -} - -sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const { - return asLayerFE(); -} - -compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() { - return mCompositionState.get(); -} - -const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const { - return mCompositionState.get(); -} - -void BufferLayer::preparePerFrameCompositionState() { - Layer::preparePerFrameCompositionState(); - - // Sideband layers - auto* compositionState = editCompositionState(); - if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) { - compositionState->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SIDEBAND; - return; - } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) { - compositionState->compositionType = - aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; - } else { - // Normal buffer layers - compositionState->hdrMetadata = mBufferInfo.mHdrMetadata; - compositionState->compositionType = mPotentialCursor - ? aidl::android::hardware::graphics::composer3::Composition::CURSOR - : aidl::android::hardware::graphics::composer3::Composition::DEVICE; - } - - compositionState->buffer = getBuffer(); - compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) - ? 0 - : mBufferInfo.mBufferSlot; - compositionState->acquireFence = mBufferInfo.mFence; - compositionState->frameNumber = mBufferInfo.mFrameNumber; - compositionState->sidebandStreamHasFrame = false; -} - -bool BufferLayer::onPreComposition(nsecs_t) { - return hasReadyFrame(); -} -namespace { -TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { - using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; - using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; - const auto frameRateCompatibility = [frameRate] { - switch (frameRate.type) { - case Layer::FrameRateCompatibility::Default: - return FrameRateCompatibility::Default; - case Layer::FrameRateCompatibility::ExactOrMultiple: - return FrameRateCompatibility::ExactOrMultiple; - default: - return FrameRateCompatibility::Undefined; - } - }(); - - const auto seamlessness = [frameRate] { - switch (frameRate.seamlessness) { - case scheduler::Seamlessness::OnlySeamless: - return Seamlessness::ShouldBeSeamless; - case scheduler::Seamlessness::SeamedAndSeamless: - return Seamlessness::NotRequired; - default: - return Seamlessness::Undefined; - } - }(); - - return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(), - .frameRateCompatibility = frameRateCompatibility, - .seamlessness = seamlessness}; -} -} // namespace - -void BufferLayer::onPostComposition(const DisplayDevice* display, - const std::shared_ptr<FenceTime>& glDoneFence, - const std::shared_ptr<FenceTime>& presentFence, - const CompositorTiming& compositorTiming) { - // mFrameLatencyNeeded is true when a new frame was latched for the - // composition. - if (!mBufferInfo.mFrameLatencyNeeded) return; - - // Update mFrameEventHistory. - finalizeFrameEventHistory(glDoneFence, compositorTiming); - - // Update mFrameTracker. - nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; - mFrameTracker.setDesiredPresentTime(desiredPresentTime); - - const int32_t layerId = getSequence(); - mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime); - - const auto outputLayer = findOutputLayerForDisplay(display); - if (outputLayer && outputLayer->requiresClientComposition()) { - nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp; - mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, - clientCompositionTimestamp, - FrameTracer::FrameEvent::FALLBACK_COMPOSITION); - // Update the SurfaceFrames in the drawing state - if (mDrawingState.bufferSurfaceFrameTX) { - mDrawingState.bufferSurfaceFrameTX->setGpuComposition(); - } - for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { - surfaceFrame->setGpuComposition(); - } - } - - std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; - if (frameReadyFence->isValid()) { - mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); - } else { - // There was no fence for this frame, so assume that it was ready - // to be presented at the desired present time. - mFrameTracker.setFrameReadyTime(desiredPresentTime); - } - - if (display) { - const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps(); - const std::optional<Fps> renderRate = - mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); - - const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate); - const auto gameMode = getGameMode(); - - if (presentFence->isValid()) { - mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, - refreshRate, renderRate, vote, gameMode); - mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, - presentFence, - FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); - } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); - displayId && mFlinger->getHwComposer().isConnected(*displayId)) { - // The HWC doesn't support present fences, so use the refresh - // timestamp instead. - const nsecs_t actualPresentTime = display->getRefreshTimestamp(); - mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, - refreshRate, renderRate, vote, gameMode); - mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), - mCurrentFrameNumber, actualPresentTime, - FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentTime(actualPresentTime); - } - } - - mFrameTracker.advanceFrame(); - mBufferInfo.mFrameLatencyNeeded = false; -} - -void BufferLayer::gatherBufferInfo() { - mBufferInfo.mPixelFormat = - !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat(); - mBufferInfo.mFrameLatencyNeeded = true; -} - -bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { - // If this is not a valid vsync for the layer's uid, return and try again later - const bool isVsyncValidForUid = - mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid); - if (!isVsyncValidForUid) { - ATRACE_NAME("!isVsyncValidForUid"); - return false; - } - - // AutoRefresh layers and sideband streams should always be presented - if (getSidebandStreamChanged() || getAutoRefresh()) { - return true; - } - - // If this layer doesn't have a frame is shouldn't be presented - if (!hasFrameUpdate()) { - return false; - } - - // Defer to the derived class to decide whether the next buffer is due for - // presentation. - return isBufferDue(expectedPresentTime); -} - -bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) { - ATRACE_CALL(); - - bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); - - if (refreshRequired) { - return refreshRequired; - } - - // If the head buffer's acquire fence hasn't signaled yet, return and - // try again later - if (!fenceHasSignaled()) { - ATRACE_NAME("!fenceHasSignaled()"); - mFlinger->onLayerUpdate(); - return false; - } - - // Capture the old state of the layer for comparisons later - const State& s(getDrawingState()); - const bool oldOpacity = isOpaque(s); - - BufferInfo oldBufferInfo = mBufferInfo; - - status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime); - if (err != NO_ERROR) { - return false; - } - - err = updateActiveBuffer(); - if (err != NO_ERROR) { - return false; - } - - err = updateFrameNumber(); - if (err != NO_ERROR) { - return false; - } - - gatherBufferInfo(); - - if (oldBufferInfo.mBuffer == nullptr) { - // the first time we receive a buffer, we need to trigger a - // geometry invalidation. - recomputeVisibleRegions = true; - } - - if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) || - (mBufferInfo.mTransform != oldBufferInfo.mTransform) || - (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) || - (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) { - recomputeVisibleRegions = true; - } - - if (oldBufferInfo.mBuffer != nullptr) { - uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); - uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); - if (bufWidth != oldBufferInfo.mBuffer->getWidth() || - bufHeight != oldBufferInfo.mBuffer->getHeight()) { - recomputeVisibleRegions = true; - } - } - - if (oldOpacity != isOpaque(s)) { - recomputeVisibleRegions = true; - } - - return true; -} - -bool BufferLayer::hasReadyFrame() const { - return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh(); -} - -uint32_t BufferLayer::getEffectiveScalingMode() const { - return mBufferInfo.mScaleMode; -} - -bool BufferLayer::isProtected() const { - return (mBufferInfo.mBuffer != nullptr) && - (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); -} - -// As documented in libhardware header, formats in the range -// 0x100 - 0x1FF are specific to the HAL implementation, and -// are known to have no alpha channel -// TODO: move definition for device-specific range into -// hardware.h, instead of using hard-coded values here. -#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) - -bool BufferLayer::getOpacityForFormat(PixelFormat format) { - if (HARDWARE_IS_DEVICE_FORMAT(format)) { - return true; - } - switch (format) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_R_8: - return false; - } - // in all other case, we have no blending (also for unknown formats) - return true; -} - -bool BufferLayer::needsFiltering(const DisplayDevice* display) const { - const auto outputLayer = findOutputLayerForDisplay(display); - if (outputLayer == nullptr) { - return false; - } - - // We need filtering if the sourceCrop rectangle size does not match the - // displayframe rectangle size (not a 1:1 render) - const auto& compositionState = outputLayer->getState(); - const auto displayFrame = compositionState.displayFrame; - const auto sourceCrop = compositionState.sourceCrop; - return sourceCrop.getHeight() != displayFrame.getHeight() || - sourceCrop.getWidth() != displayFrame.getWidth(); -} - -bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display, - const ui::Transform& inverseParentTransform) const { - const auto outputLayer = findOutputLayerForDisplay(display); - if (outputLayer == nullptr) { - return false; - } - - // We need filtering if the sourceCrop rectangle size does not match the - // viewport rectangle size (not a 1:1 render) - const auto& compositionState = outputLayer->getState(); - const ui::Transform& displayTransform = display->getTransform(); - const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse(); - // Undo the transformation of the displayFrame so that we're back into - // layer-stack space. - const Rect frame = inverseTransform.transform(compositionState.displayFrame); - const FloatRect sourceCrop = compositionState.sourceCrop; - - int32_t frameHeight = frame.getHeight(); - int32_t frameWidth = frame.getWidth(); - // If the display transform had a rotational component then undo the - // rotation so that the orientation matches the source crop. - if (displayTransform.getOrientation() & ui::Transform::ROT_90) { - std::swap(frameHeight, frameWidth); - } - return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth; -} - -Rect BufferLayer::getBufferSize(const State& s) const { - // If we have a sideband stream, or we are scaling the buffer then return the layer size since - // we cannot determine the buffer size. - if ((s.sidebandStream != nullptr) || - (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) { - return Rect(getActiveWidth(s), getActiveHeight(s)); - } - - if (mBufferInfo.mBuffer == nullptr) { - return Rect::INVALID_RECT; - } - - uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); - uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); - - // Undo any transformations on the buffer and return the result. - if (mBufferInfo.mTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - - if (getTransformToDisplayInverse()) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - } - - return Rect(bufWidth, bufHeight); -} - -FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const { - const State& s(getDrawingState()); - - // If we have a sideband stream, or we are scaling the buffer then return the layer size since - // we cannot determine the buffer size. - if ((s.sidebandStream != nullptr) || - (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) { - return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s)); - } - - if (mBufferInfo.mBuffer == nullptr) { - return parentBounds; - } - - uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); - uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); - - // Undo any transformations on the buffer and return the result. - if (mBufferInfo.mTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - - if (getTransformToDisplayInverse()) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - } - - return FloatRect(0, 0, bufWidth, bufHeight); -} - -void BufferLayer::latchAndReleaseBuffer() { - if (hasReadyFrame()) { - bool ignored = false; - latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */); - } - releasePendingBuffer(systemTime()); -} - -PixelFormat BufferLayer::getPixelFormat() const { - return mBufferInfo.mPixelFormat; -} - -bool BufferLayer::getTransformToDisplayInverse() const { - return mBufferInfo.mTransformToDisplayInverse; -} - -Rect BufferLayer::getBufferCrop() const { - // this is the crop rectangle that applies to the buffer - // itself (as opposed to the window) - if (!mBufferInfo.mCrop.isEmpty()) { - // if the buffer crop is defined, we use that - return mBufferInfo.mCrop; - } else if (mBufferInfo.mBuffer != nullptr) { - // otherwise we use the whole buffer - return mBufferInfo.mBuffer->getBounds(); - } else { - // if we don't have a buffer yet, we use an empty/invalid crop - return Rect(); - } -} - -uint32_t BufferLayer::getBufferTransform() const { - return mBufferInfo.mTransform; -} - -ui::Dataspace BufferLayer::getDataSpace() const { - return mBufferInfo.mDataspace; -} - -ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) { - ui::Dataspace updatedDataspace = dataspace; - // translate legacy dataspaces to modern dataspaces - switch (dataspace) { - case ui::Dataspace::SRGB: - updatedDataspace = ui::Dataspace::V0_SRGB; - break; - case ui::Dataspace::SRGB_LINEAR: - updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR; - break; - case ui::Dataspace::JFIF: - updatedDataspace = ui::Dataspace::V0_JFIF; - break; - case ui::Dataspace::BT601_625: - updatedDataspace = ui::Dataspace::V0_BT601_625; - break; - case ui::Dataspace::BT601_525: - updatedDataspace = ui::Dataspace::V0_BT601_525; - break; - case ui::Dataspace::BT709: - updatedDataspace = ui::Dataspace::V0_BT709; - break; - default: - break; - } - - return updatedDataspace; -} - -sp<GraphicBuffer> BufferLayer::getBuffer() const { - return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr; -} - -void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) { - GLConsumer::computeTransformMatrix(outMatrix, - mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() - : nullptr, - mBufferInfo.mCrop, mBufferInfo.mTransform, filteringEnabled); -} - -void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { - Layer::setInitialValuesForClone(clonedFrom); - - sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get()); - mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha; - mPotentialCursor = bufferClonedFrom->mPotentialCursor; - mProtectedByApp = bufferClonedFrom->mProtectedByApp; - - updateCloneBufferInfo(); -} - -void BufferLayer::updateCloneBufferInfo() { - if (!isClone() || !isClonedFromAlive()) { - return; - } - - sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get()); - mBufferInfo = clonedFrom->mBufferInfo; - mSidebandStream = clonedFrom->mSidebandStream; - surfaceDamageRegion = clonedFrom->surfaceDamageRegion; - mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load(); - mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber; - - // After buffer info is updated, the drawingState from the real layer needs to be copied into - // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives - // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in - // the cloned drawingState again. - wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; - SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; - wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; - WindowInfo tmpInputInfo = mDrawingState.inputInfo; - - cloneDrawingState(clonedFrom.get()); - - mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; - mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; - mDrawingState.zOrderRelatives = tmpZOrderRelatives; - mDrawingState.inputInfo = tmpInputInfo; -} - -void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) { - mTransformHint = getFixedTransformHint(); - if (mTransformHint == ui::Transform::ROT_INVALID) { - mTransformHint = displayTransformHint; - } -} - -bool BufferLayer::bufferNeedsFiltering() const { - return isFixedSize(); -} - -const std::shared_ptr<renderengine::ExternalTexture>& BufferLayer::getExternalTexture() const { - return mBufferInfo.mBuffer; -} - -} // namespace android - -#if defined(__gl_h_) -#error "don't include gl/gl.h in this file" -#endif - -#if defined(__gl2_h_) -#error "don't include gl2/gl2.h in this file" -#endif - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h deleted file mode 100644 index 4c70eb58bf..0000000000 --- a/services/surfaceflinger/BufferLayer.h +++ /dev/null @@ -1,222 +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. - */ - -#pragma once - -#include <sys/types.h> -#include <cstdint> -#include <list> - -#include <gui/ISurfaceComposerClient.h> -#include <gui/LayerState.h> -#include <renderengine/Image.h> -#include <renderengine/Mesh.h> -#include <renderengine/Texture.h> -#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE -#include <ui/FrameStats.h> -#include <ui/GraphicBuffer.h> -#include <ui/PixelFormat.h> -#include <ui/Region.h> -#include <utils/RefBase.h> -#include <utils/String8.h> -#include <utils/Timers.h> - -#include "BufferLayerConsumer.h" -#include "Client.h" -#include "DisplayHardware/HWComposer.h" -#include "FrameTimeline.h" -#include "FrameTracker.h" -#include "Layer.h" -#include "LayerVector.h" -#include "MonitoredProducer.h" -#include "SurfaceFlinger.h" - -namespace android { - -class BufferLayer : public Layer { -public: - explicit BufferLayer(const LayerCreationArgs& args); - virtual ~BufferLayer() override; - - // Implements Layer. - sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override; - compositionengine::LayerFECompositionState* editCompositionState() override; - - // If we have received a new buffer this frame, we will pass its surface - // damage down to hardware composer. Otherwise, we must send a region with - // one empty rect. - void useSurfaceDamage() override; - void useEmptyDamage() override; - - bool isOpaque(const Layer::State& s) const override; - bool canReceiveInput() const override; - - // isVisible - true if this layer is visible, false otherwise - bool isVisible() const override; - - // isProtected - true if the layer may contain protected content in the - // GRALLOC_USAGE_PROTECTED sense. - bool isProtected() const override; - - // isFixedSize - true if content has a fixed size - bool isFixedSize() const override; - - bool usesSourceCrop() const override; - - bool isHdrY410() const override; - - void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence, - const std::shared_ptr<FenceTime>& presentFence, - const CompositorTiming&) override; - - // latchBuffer - called each time the screen is redrawn and returns whether - // 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. - bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) override; - bool hasReadyFrame() const override; - - // Returns the current scaling mode - uint32_t getEffectiveScalingMode() const override; - - // Calls latchBuffer if the buffer has a frame queued and then releases the buffer. - // This is used if the buffer is just latched and releases to free up the buffer - // and will not be shown on screen. - // Should only be called on the main thread. - void latchAndReleaseBuffer() override; - - bool getTransformToDisplayInverse() const override; - - Rect getBufferCrop() const override; - - uint32_t getBufferTransform() const override; - - ui::Dataspace getDataSpace() const override; - - sp<GraphicBuffer> getBuffer() const override; - const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override; - - ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; } - - // Returns true if the transformed buffer size does not match the layer size and we need - // to apply filtering. - virtual bool bufferNeedsFiltering() const; - -protected: - struct BufferInfo { - nsecs_t mDesiredPresentTime; - std::shared_ptr<FenceTime> mFenceTime; - sp<Fence> mFence; - uint32_t mTransform{0}; - ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN}; - Rect mCrop; - uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; - Region mSurfaceDamage; - HdrMetadata mHdrMetadata; - int mApi; - PixelFormat mPixelFormat{PIXEL_FORMAT_NONE}; - bool mTransformToDisplayInverse{false}; - - std::shared_ptr<renderengine::ExternalTexture> mBuffer; - uint64_t mFrameNumber; - int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT}; - - bool mFrameLatencyNeeded{false}; - }; - - BufferInfo mBufferInfo; - virtual void gatherBufferInfo() = 0; - - std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings&) override; - - /* - * compositionengine::LayerFE overrides - */ - const compositionengine::LayerFECompositionState* getCompositionState() const override; - bool onPreComposition(nsecs_t) override; - void preparePerFrameCompositionState() override; - - static bool getOpacityForFormat(PixelFormat format); - - // from graphics API - const uint32_t mTextureName; - ui::Dataspace translateDataspace(ui::Dataspace dataspace); - void setInitialValuesForClone(const sp<Layer>& clonedFrom); - void updateCloneBufferInfo() override; - uint64_t mPreviousFrameNumber = 0; - - void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; - - // Transform hint provided to the producer. This must be accessed holding - // the mStateLock. - ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0; - - bool getAutoRefresh() const { return mDrawingState.autoRefresh; } - bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } - - // Returns true if the next buffer should be presented at the expected present time - bool shouldPresentNow(nsecs_t expectedPresentTime) const; - - // Returns true if the next buffer should be presented at the expected present time, - // overridden by BufferStateLayer and BufferQueueLayer for implementation - // specific logic - virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0; - - std::atomic<bool> mSidebandStreamChanged{false}; - -private: - virtual bool fenceHasSignaled() const = 0; - virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; - - // Latch sideband stream and returns true if the dirty region should be updated. - virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0; - - virtual bool hasFrameUpdate() const = 0; - - virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) = 0; - - virtual status_t updateActiveBuffer() = 0; - virtual status_t updateFrameNumber() = 0; - - // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they - // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion - // detection. - bool needsInputInfo() const override { return !mPotentialCursor; } - - // Returns true if this layer requires filtering - bool needsFiltering(const DisplayDevice*) const override; - bool needsFilteringForScreenshots(const DisplayDevice*, - const ui::Transform& inverseParentTransform) const override; - - // 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; - - PixelFormat getPixelFormat() const; - - // Computes the transform matrix using the setFilteringEnabled to determine whether the - // transform matrix should be computed for use with bilinear filtering. - void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]); - - std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState; - - FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; -}; - -} // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp deleted file mode 100644 index 7361a4fdef..0000000000 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#undef LOG_TAG -#define LOG_TAG "BufferLayerConsumer" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -#include "BufferLayerConsumer.h" -#include "Layer.h" -#include "Scheduler/VsyncController.h" - -#include <inttypes.h> - -#include <cutils/compiler.h> - -#include <hardware/hardware.h> - -#include <math/mat4.h> - -#include <gui/BufferItem.h> -#include <gui/GLConsumer.h> -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> -#include <private/gui/ComposerService.h> -#include <renderengine/RenderEngine.h> -#include <renderengine/impl/ExternalTexture.h> -#include <utils/Log.h> -#include <utils/String8.h> -#include <utils/Trace.h> - -namespace android { - -// Macros for including the BufferLayerConsumer name in log messages -#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) - -static const mat4 mtxIdentity; - -BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, - renderengine::RenderEngine& engine, uint32_t tex, - Layer* layer) - : ConsumerBase(bq, false), - mCurrentCrop(Rect::EMPTY_RECT), - mCurrentTransform(0), - mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mCurrentFence(Fence::NO_FENCE), - mCurrentTimestamp(0), - mCurrentDataSpace(ui::Dataspace::UNKNOWN), - mCurrentFrameNumber(0), - mCurrentTransformToDisplayInverse(false), - mCurrentSurfaceDamage(), - mCurrentApi(0), - mDefaultWidth(1), - mDefaultHeight(1), - mFilteringEnabled(true), - mRE(engine), - mTexName(tex), - mLayer(layer), - mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) { - BLC_LOGV("BufferLayerConsumer"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!"); - return NO_INIT; - } - mDefaultWidth = w; - mDefaultHeight = h; - return mConsumer->setDefaultBufferSize(w, h); -} - -void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) { - setFrameAvailableListener(listener); - Mutex::Autolock lock(mMutex); - mContentsChangedListener = listener; -} - -status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, - bool* autoRefresh, bool* queuedBuffer, - uint64_t maxFrameNumber) { - ATRACE_CALL(); - BLC_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!"); - return NO_INIT; - } - - BufferItem item; - - // Acquire the next buffer. - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber); - if (err != NO_ERROR) { - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - err = NO_ERROR; - } else if (err == BufferQueue::PRESENT_LATER) { - // return the error, without logging - } else { - BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); - } - return err; - } - - if (autoRefresh) { - *autoRefresh = item.mAutoRefresh; - } - - if (queuedBuffer) { - *queuedBuffer = item.mQueuedBuffer; - } - - // We call the rejecter here, in case the caller has a reason to - // not accept this buffer. This is used by SurfaceFlinger to - // reject buffers which have the wrong size - int slot = item.mSlot; - if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) { - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); - return BUFFER_REJECTED; - } - - // Release the previous buffer. - err = updateAndReleaseLocked(item, &mPendingRelease); - if (err != NO_ERROR) { - return err; - } - return err; -} - -void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { - if (!fence->isValid()) { - return; - } - - auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture; - if (slot == BufferQueue::INVALID_BUFFER_SLOT) { - return; - } - - auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer - : mCurrentTextureBuffer->getBuffer(); - auto err = addReleaseFence(slot, buffer, fence); - if (err != OK) { - BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); - } -} - -bool BufferLayerConsumer::releasePendingBuffer() { - if (!mPendingRelease.isPending) { - BLC_LOGV("Pending buffer already released"); - return false; - } - BLC_LOGV("Releasing pending buffer"); - Mutex::Autolock lock(mMutex); - status_t result = - releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer); - if (result < NO_ERROR) { - BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result); - } - mPendingRelease = PendingRelease(); - return true; -} - -sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const { - Mutex::Autolock lock(mMutex); - return ConsumerBase::mPrevFinalReleaseFence; -} - -status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); - if (err != NO_ERROR) { - return err; - } - - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so we need to clean up old references. - if (item->mGraphicBuffer != nullptr) { - std::lock_guard<std::mutex> lock(mImagesMutex); - if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr || - mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) { - mImages[item->mSlot] = std::make_shared< - renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE, - renderengine::impl::ExternalTexture:: - Usage::READABLE); - } - } - - return NO_ERROR; -} - -status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, - PendingRelease* pendingRelease) { - status_t err = NO_ERROR; - - int slot = item.mSlot; - - BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr) - ? mCurrentTextureBuffer->getBuffer()->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. - - std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer; - { - std::lock_guard<std::mutex> lock(mImagesMutex); - nextTextureBuffer = mImages[slot]; - } - - // release old buffer - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (pendingRelease == nullptr) { - status_t status = - releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer()); - if (status < NO_ERROR) { - BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), - status); - err = status; - // keep going, with error raised [?] - } - } else { - pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer(); - pendingRelease->isPending = true; - } - } - - // Update the BufferLayerConsumer state. - mCurrentTexture = slot; - mCurrentTextureBuffer = nextTextureBuffer; - mCurrentCrop = item.mCrop; - mCurrentTransform = item.mTransform; - mCurrentScalingMode = item.mScalingMode; - mCurrentTimestamp = item.mTimestamp; - mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace); - mCurrentHdrMetadata = item.mHdrMetadata; - mCurrentFence = item.mFence; - mCurrentFenceTime = item.mFenceTime; - mCurrentFrameNumber = item.mFrameNumber; - mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse; - mCurrentSurfaceDamage = item.mSurfaceDamage; - mCurrentApi = item.mApi; - - computeCurrentTransformMatrixLocked(); - - return err; -} - -void BufferLayerConsumer::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void BufferLayerConsumer::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTextureBuffer == nullptr) { - BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr"); - } - - if (needsRecompute && mCurrentTextureBuffer != nullptr) { - computeCurrentTransformMatrixLocked(); - } -} - -void BufferLayerConsumer::computeCurrentTransformMatrixLocked() { - BLC_LOGV("computeCurrentTransformMatrixLocked"); - if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) { - BLC_LOGD("computeCurrentTransformMatrixLocked: " - "mCurrentTextureBuffer is nullptr"); - } - GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, - mCurrentTextureBuffer == nullptr - ? nullptr - : mCurrentTextureBuffer->getBuffer(), - getCurrentCropLocked(), mCurrentTransform, - mFilteringEnabled); -} - -nsecs_t BufferLayerConsumer::getTimestamp() { - BLC_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() { - BLC_LOGV("getCurrentDataSpace"); - Mutex::Autolock lock(mMutex); - return mCurrentDataSpace; -} - -const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const { - BLC_LOGV("getCurrentHdrMetadata"); - Mutex::Autolock lock(mMutex); - return mCurrentHdrMetadata; -} - -uint64_t BufferLayerConsumer::getFrameNumber() { - BLC_LOGV("getFrameNumber"); - Mutex::Autolock lock(mMutex); - return mCurrentFrameNumber; -} - -bool BufferLayerConsumer::getTransformToDisplayInverse() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransformToDisplayInverse; -} - -const Region& BufferLayerConsumer::getSurfaceDamage() const { - return mCurrentSurfaceDamage; -} - -void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) { - if (damage.bounds() == Rect::INVALID_RECT || - mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) { - mCurrentSurfaceDamage = Region::INVALID_REGION; - } else { - mCurrentSurfaceDamage |= damage; - } -} - -int BufferLayerConsumer::getCurrentApi() const { - Mutex::Autolock lock(mMutex); - return mCurrentApi; -} - -std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer( - int* outSlot, sp<Fence>* outFence) const { - Mutex::Autolock lock(mMutex); - - if (outSlot != nullptr) { - *outSlot = mCurrentTexture; - } - - if (outFence != nullptr) { - *outFence = mCurrentFence; - } - - return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer; -} - -Rect BufferLayerConsumer::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - return getCurrentCropLocked(); -} - -Rect BufferLayerConsumer::getCurrentCropLocked() const { - uint32_t width = mDefaultWidth; - uint32_t height = mDefaultHeight; - // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order - // to scale and crop correctly. - if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - width = mDefaultHeight; - height = mDefaultWidth; - } - - return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) - ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height) - : mCurrentCrop; -} - -uint32_t BufferLayerConsumer::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t BufferLayerConsumer::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp<Fence> BufferLayerConsumer::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const { - Mutex::Autolock lock(mMutex); - return mCurrentFenceTime; -} - -void BufferLayerConsumer::freeBufferLocked(int slotIndex) { - BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - std::lock_guard<std::mutex> lock(mImagesMutex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - mImages[slotIndex] = nullptr; - ConsumerBase::freeBufferLocked(slotIndex); -} - -void BufferLayerConsumer::onDisconnect() { - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // Nothing to do if we're already abandoned. - return; - } - - mLayer->onDisconnect(); -} - -void BufferLayerConsumer::onSidebandStreamChanged() { - [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr; - { - Mutex::Autolock lock(mFrameAvailableMutex); - unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get(); - } - sp<ContentsChangedListener> listener; - { // scope for the lock - Mutex::Autolock lock(mMutex); - ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get()); - listener = mContentsChangedListener.promote(); - } - - if (listener != nullptr) { - listener->onSidebandStreamChanged(); - } -} - -void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) { - if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) { - std::lock_guard<std::mutex> lock(mImagesMutex); - const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot]; - if (oldImage == nullptr || oldImage->getBuffer() == nullptr || - oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) { - mImages[item.mSlot] = std::make_shared< - renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE, - renderengine::impl::ExternalTexture:: - Usage::READABLE); - } - } -} - -void BufferLayerConsumer::abandonLocked() { - BLC_LOGV("abandonLocked"); - mCurrentTextureBuffer = nullptr; - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - std::lock_guard<std::mutex> lock(mImagesMutex); - mImages[i] = nullptr; - } - ConsumerBase::abandonLocked(); -} - -status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) { - return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); -} - -void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const { - result.appendFormat("%smTexName=%d mCurrentTexture=%d\n" - "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", - prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, - mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, - mCurrentTransform); - - ConsumerBase::dumpLocked(result, prefix); -} -}; // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h deleted file mode 100644 index 23ad2a3f91..0000000000 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2010 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_BUFFERLAYERCONSUMER_H -#define ANDROID_BUFFERLAYERCONSUMER_H - -#include <android-base/thread_annotations.h> -#include <gui/BufferQueueDefs.h> -#include <gui/ConsumerBase.h> -#include <gui/HdrMetadata.h> -#include <renderengine/ExternalTexture.h> -#include <ui/FenceTime.h> -#include <ui/GraphicBuffer.h> -#include <ui/GraphicTypes.h> -#include <ui/Region.h> -#include <utils/String8.h> -#include <utils/Vector.h> -#include <utils/threads.h> - -namespace android { -// ---------------------------------------------------------------------------- - -class Layer; -class String8; - -namespace renderengine { -class RenderEngine; -} // namespace renderengine - -/* - * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue, - * and makes them available to RenderEngine as a texture. - * - * A typical usage pattern is to call updateTexImage() when a new frame is - * desired. If a new frame is available, the frame is latched. If not, the - * previous contents are retained. The texture is attached and updated after - * bindTextureImage() is called. - * - * All calls to updateTexImage must be made with RenderEngine being current. - * The texture is attached to the TEXTURE_EXTERNAL texture target. - */ -class BufferLayerConsumer : public ConsumerBase { -public: - static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8; - - class BufferRejecter { - friend class BufferLayerConsumer; - virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0; - - protected: - virtual ~BufferRejecter() {} - }; - - struct ContentsChangedListener : public FrameAvailableListener { - virtual void onSidebandStreamChanged() = 0; - }; - - // BufferLayerConsumer constructs a new BufferLayerConsumer object. The - // tex parameter indicates the name of the RenderEngine texture to which - // images are to be streamed. - BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine, - uint32_t tex, Layer* layer); - - // Sets the contents changed listener. This should be used instead of - // ConsumerBase::setFrameAvailableListener(). - void setContentsChangedListener(const wp<ContentsChangedListener>& listener); - - // updateTexImage acquires the most recently queued buffer, and sets the - // image contents of the target texture to it. - // - // This call may only be made while RenderEngine is current. - // - // This calls doFenceWait to ensure proper synchronization unless native - // fence is supported. - // - // Unlike the GLConsumer version, this version takes a functor that may be - // 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); - - // setReleaseFence stores a fence that will signal when the current buffer - // is no longer being read. This fence will be returned to the producer - // when the current buffer is released by updateTexImage(). Multiple - // fences can be set for a given buffer; they will be merged into a single - // union fence. - void setReleaseFence(const sp<Fence>& fence); - - bool releasePendingBuffer(); - - sp<Fence> getPrevFinalReleaseFence() const; - - // See GLConsumer::getTransformMatrix. - void getTransformMatrix(float mtx[16]); - - // getTimestamp retrieves the timestamp associated with the texture image - // set by the most recent call to updateTexImage. - // - // The timestamp is in nanoseconds, and is monotonically increasing. Its - // other semantics (zero point, etc) are source-dependent and should be - // documented by the source. - int64_t getTimestamp(); - - // getDataSpace retrieves the DataSpace associated with the texture image - // set by the most recent call to updateTexImage. - ui::Dataspace getCurrentDataSpace(); - - // getCurrentHdrMetadata retrieves the HDR metadata associated with the - // texture image set by the most recent call to updateTexImage. - const HdrMetadata& getCurrentHdrMetadata() const; - - // getFrameNumber retrieves the frame number associated with the texture - // image set by the most recent call to updateTexImage. - // - // The frame number is an incrementing counter set to 0 at the creation of - // the BufferQueue associated with this consumer. - uint64_t getFrameNumber(); - - bool getTransformToDisplayInverse() const; - - // must be called from SF main thread - const Region& getSurfaceDamage() const; - - // Merge the given damage region into the current damage region value. - void mergeSurfaceDamage(const Region& damage); - - // getCurrentApi retrieves the API which queues the current buffer. - int getCurrentApi() const; - - // See GLConsumer::setDefaultBufferSize. - status_t setDefaultBufferSize(uint32_t width, uint32_t height); - - // setFilteringEnabled sets whether the transform matrix should be computed - // for use with bilinear filtering. - void setFilteringEnabled(bool enabled); - - // 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 - // fence is returned. - std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer( - int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const; - - // getCurrentCrop returns the cropping rectangle of the current buffer. - Rect getCurrentCrop() const; - - // getCurrentTransform returns the transform of the current buffer. - uint32_t getCurrentTransform() const; - - // getCurrentScalingMode returns the scaling mode of the current buffer. - uint32_t getCurrentScalingMode() const; - - // getCurrentFence returns the fence indicating when the current buffer is - // ready to be read from. - sp<Fence> getCurrentFence() const; - - // getCurrentFence returns the FenceTime indicating when the current - // buffer is ready to be read from. - std::shared_ptr<FenceTime> getCurrentFenceTime() const; - - // setConsumerUsageBits overrides the ConsumerBase method to OR - // DEFAULT_USAGE_FLAGS to usage. - status_t setConsumerUsageBits(uint64_t usage); - void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex); - -protected: - // abandonLocked overrides the ConsumerBase method to clear - // mCurrentTextureImage in addition to the ConsumerBase behavior. - virtual void abandonLocked() EXCLUDES(mImagesMutex); - - // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer- - // specific info in addition to the ConsumerBase behavior. - virtual void dumpLocked(String8& result, const char* prefix) const; - - // See ConsumerBase::acquireBufferLocked - virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override - EXCLUDES(mImagesMutex); - - bool canUseImageCrop(const Rect& crop) const; - - struct PendingRelease { - PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {} - - bool isPending; - int currentTexture; - sp<GraphicBuffer> graphicBuffer; - }; - - // This releases the buffer in the slot referenced by mCurrentTexture, - // then updates state to refer to the BufferItem, which must be a - // newly-acquired buffer. If pendingRelease is not null, the parameters - // which would have been passed to releaseBufferLocked upon the successful - // 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) - EXCLUDES(mImagesMutex); - -private: - // Utility class for managing GraphicBuffer references into renderengine - class Image { - public: - Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& 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. - // - // This method must be called with mMutex locked. - virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex); - - // IConsumerListener interface - void onDisconnect() override; - void onSidebandStreamChanged() override; - void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {} - - // computeCurrentTransformMatrixLocked computes the transform matrix for the - // current texture. It uses mCurrentTransform and the current GraphicBuffer - // to compute this matrix and stores it in mCurrentTransformMatrix. - // mCurrentTextureImage must not be nullptr. - void computeCurrentTransformMatrixLocked(); - - // getCurrentCropLocked returns the cropping rectangle of the current buffer. - Rect getCurrentCropLocked() const; - - // 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; - - // 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. - std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer; - - // mCurrentCrop is the crop rectangle that applies to the current texture. - // It gets set each time updateTexImage is called. - Rect mCurrentCrop; - - // mCurrentTransform is the transform identifier for the current texture. It - // gets set each time updateTexImage is called. - uint32_t mCurrentTransform; - - // mCurrentScalingMode is the scaling mode for the current texture. It gets - // set each time updateTexImage is called. - uint32_t mCurrentScalingMode; - - // mCurrentFence is the fence received from BufferQueue in updateTexImage. - sp<Fence> mCurrentFence; - - // The FenceTime wrapper around mCurrentFence. - std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE}; - - // mCurrentTransformMatrix is the transform matrix for the current texture. - // It gets computed by computeTransformMatrix each time updateTexImage is - // called. - float mCurrentTransformMatrix[16]; - - // mCurrentTimestamp is the timestamp for the current texture. It - // gets set each time updateTexImage is called. - int64_t mCurrentTimestamp; - - // mCurrentDataSpace is the dataspace for the current texture. It - // gets set each time updateTexImage is called. - ui::Dataspace mCurrentDataSpace; - - // mCurrentHdrMetadata is the HDR metadata for the current texture. It - // gets set each time updateTexImage is called. - HdrMetadata mCurrentHdrMetadata; - - // mCurrentFrameNumber is the frame counter for the current texture. - // It gets set each time updateTexImage is called. - uint64_t mCurrentFrameNumber; - - // Indicates this buffer must be transformed by the inverse transform of the screen - // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform. - // This must be set/read from SurfaceFlinger's main thread. - bool mCurrentTransformToDisplayInverse; - - // The portion of this surface that has changed since the previous frame - Region mCurrentSurfaceDamage; - - int mCurrentApi; - - uint32_t mDefaultWidth, mDefaultHeight; - - // mFilteringEnabled indicates whether the transform matrix is computed for - // use with bilinear filtering. It defaults to true and is changed by - // setFilteringEnabled(). - bool mFilteringEnabled; - - renderengine::RenderEngine& mRE; - - // mTexName is the name of the RenderEngine texture to which streamed - // images will be bound when bindTexImage is called. It is set at - // construction time. - const uint32_t mTexName; - - // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing. - Layer* mLayer GUARDED_BY(mMutex); - - wp<ContentsChangedListener> mContentsChangedListener; - - // mCurrentTexture is the buffer slot index of the buffer that is currently - // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT, - // indicating that no buffer slot is currently bound to the texture. Note, - // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - // that no buffer is bound to the texture. A call to setBufferCount will - // reset mCurrentTexture to INVALID_BUFFER_SLOT. - int mCurrentTexture; - - // Shadow buffer cache for cleaning up renderengine references. - std::shared_ptr<renderengine::ExternalTexture> - mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex); - - // Separate mutex guarding the shadow buffer cache. - // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated) - // which is contentious enough that we can't just use mMutex. - mutable std::mutex mImagesMutex; - - // A release that is pending on the receipt of a new release fence from - // presentDisplay - PendingRelease mPendingRelease; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_BUFFERLAYERCONSUMER_H diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp deleted file mode 100644 index bee4de32a6..0000000000 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ /dev/null @@ -1,575 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#undef LOG_TAG -#define LOG_TAG "BufferQueueLayer" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "BufferQueueLayer.h" - -#include <compositionengine/LayerFECompositionState.h> -#include <gui/BufferQueueConsumer.h> -#include <system/window.h> - -#include "LayerRejecter.h" -#include "SurfaceInterceptor.h" - -#include "FrameTracer/FrameTracer.h" -#include "Scheduler/LayerHistory.h" -#include "TimeStats/TimeStats.h" - -namespace android { -using PresentState = frametimeline::SurfaceFrame::PresentState; - -BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {} - -BufferQueueLayer::~BufferQueueLayer() { - mContentsChangedListener->abandon(); - mConsumer->abandon(); -} - -// ----------------------------------------------------------------------- -// Interface implementation for Layer -// ----------------------------------------------------------------------- - -void BufferQueueLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { - const sp<Fence> releaseFence = futureFenceResult.get().value_or(Fence::NO_FENCE); - mConsumer->setReleaseFence(releaseFence); - - // Prevent tracing the same release multiple times. - if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) { - mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber, - std::make_shared<FenceTime>(releaseFence), - FrameTracer::FrameEvent::RELEASE_FENCE); - mPreviousReleasedFrameNumber = mPreviousFrameNumber; - } -} - -void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) { - BufferLayer::setTransformHint(displayTransformHint); - mConsumer->setTransformHint(mTransformHint); -} - -void BufferQueueLayer::releasePendingBuffer(nsecs_t) { - if (!mConsumer->releasePendingBuffer()) { - return; - } -} - -void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) { - mConsumer->setDefaultBufferSize(w, h); -} - -int32_t BufferQueueLayer::getQueuedFrameCount() const { - return mQueuedFrames; -} - -bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const { - Mutex::Autolock lock(mQueueItemLock); - - const int64_t addedTime = mQueueItems[0].item.mTimestamp; - - // Ignore timestamps more than a second in the future - const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1)); - ALOGW_IF(!isPlausible, - "[%s] Timestamp %" PRId64 " seems implausible " - "relative to expectedPresent %" PRId64, - getDebugName(), addedTime, expectedPresentTime); - - if (!isPlausible) { - mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence()); - } - - const bool isDue = addedTime < expectedPresentTime; - return isDue || !isPlausible; -} - -// ----------------------------------------------------------------------- -// Interface implementation for BufferLayer -// ----------------------------------------------------------------------- - -bool BufferQueueLayer::fenceHasSignaled() const { - Mutex::Autolock lock(mQueueItemLock); - - if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { - return true; - } - - if (!hasFrameUpdate()) { - return true; - } - - if (mQueueItems[0].item.mIsDroppable) { - // Even though this buffer's fence may not have signaled yet, it could - // be replaced by another buffer before it has a chance to, which means - // that it's possible to get into a situation where a buffer is never - // able to be latched. To avoid this, grab this buffer anyway. - return true; - } - const bool fenceSignaled = - mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; - if (!fenceSignaled) { - mFlinger->mTimeStats->incrementLatchSkipped(getSequence(), - TimeStats::LatchSkipReason::LateAcquire); - } - - return fenceSignaled; -} - -bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { - Mutex::Autolock lock(mQueueItemLock); - - if (!hasFrameUpdate() || isRemovedFromCurrentState()) { - return true; - } - - return mQueueItems[0].item.mTimestamp <= expectedPresentTime; -} - -bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { - // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); - - bool sidebandStreamChanged = true; - if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { - // mSidebandStreamChanged was changed to false - mSidebandStream = mConsumer->getSidebandStream(); - auto* layerCompositionState = editCompositionState(); - layerCompositionState->sidebandStream = mSidebandStream; - if (layerCompositionState->sidebandStream != nullptr) { - setTransactionFlags(eTransactionNeeded); - mFlinger->setTransactionFlags(eTraversalNeeded); - } - recomputeVisibleRegions = true; - - return true; - } - return false; -} - -bool BufferQueueLayer::hasFrameUpdate() const { - return mQueuedFrames > 0; -} - -status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) { - // 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 - // buffer mode. - bool queuedBuffer = false; - const int32_t layerId = getSequence(); - LayerRejecter r(mDrawingState, getDrawingState(), recomputeVisibleRegions, - getProducerStickyTransform() != 0, mName, - getTransformToDisplayInverse()); - - if (isRemovedFromCurrentState()) { - expectedPresentTime = 0; - } - - // updateTexImage() below might drop the some buffers at the head of the queue if there is a - // buffer behind them which is timely to be presented. However this buffer may not be signaled - // yet. The code below makes sure that this wouldn't happen by setting maxFrameNumber to the - // last buffer that was signaled. - uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived; - { - Mutex::Autolock lock(mQueueItemLock); - for (size_t i = 0; i < mQueueItems.size(); i++) { - bool fenceSignaled = - mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; - if (!fenceSignaled) { - break; - } - lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber; - } - } - const uint64_t maxFrameNumberToAcquire = - std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber); - - bool autoRefresh; - status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh, - &queuedBuffer, maxFrameNumberToAcquire); - mDrawingState.autoRefresh = autoRefresh; - 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. - mFlinger->onLayerUpdate(); - return BAD_VALUE; - } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) { - // If the buffer has been rejected, remove it from the shadow queue - // and return early - if (queuedBuffer) { - Mutex::Autolock lock(mQueueItemLock); - if (mQueuedFrames > 0) { - mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); - mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); - if (mQueueItems[0].surfaceFrame) { - addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame); - } - mQueueItems.erase(mQueueItems.begin()); - mQueuedFrames--; - } - } - return BAD_VALUE; - } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) { - // This can occur if something goes wrong when trying to create the - // EGLImage for this buffer. If this happens, the buffer has already - // been released, so we need to clean up the queue and bug out - // early. - if (queuedBuffer) { - Mutex::Autolock lock(mQueueItemLock); - for (auto& [item, surfaceFrame] : mQueueItems) { - if (surfaceFrame) { - addSurfaceFrameDroppedForBuffer(surfaceFrame); - } - } - mQueueItems.clear(); - mQueuedFrames = 0; - mFlinger->mTimeStats->onDestroy(layerId); - mFlinger->mFrameTracer->onDestroy(layerId); - } - - // Once we have hit this state, the shadow queue may no longer - // correctly reflect the incoming BufferQueue's contents, so even if - // updateTexImage starts working, the only safe course of action is - // to continue to ignore updates. - mUpdateTexImageFailed = true; - - return BAD_VALUE; - } - - bool more_frames_pending = false; - if (queuedBuffer) { - // Autolock scope - auto currentFrameNumber = mConsumer->getFrameNumber(); - - Mutex::Autolock lock(mQueueItemLock); - - // Remove any stale buffers that have been dropped during - // updateTexImage - while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) { - mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); - mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); - if (mQueueItems[0].surfaceFrame) { - addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame); - } - mQueueItems.erase(mQueueItems.begin()); - mQueuedFrames--; - } - - uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId(); - mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime, - FrameTracer::FrameEvent::LATCH); - - if (mQueueItems[0].surfaceFrame) { - addSurfaceFramePresentedForBuffer(mQueueItems[0].surfaceFrame, - mQueueItems[0].item.mFenceTime->getSignalTime(), - latchTime); - } - mQueueItems.erase(mQueueItems.begin()); - more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1); - } - - // Decrement the queued-frames count. Signal another event if we - // have more frames pending. - if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) { - mFlinger->onLayerUpdate(); - } - - return NO_ERROR; -} - -status_t BufferQueueLayer::updateActiveBuffer() { - // update the active buffer - mPreviousBufferId = getCurrentBufferId(); - mBufferInfo.mBuffer = - mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence); - - if (mBufferInfo.mBuffer == nullptr) { - // this can only happen if the very first buffer was rejected. - return BAD_VALUE; - } - return NO_ERROR; -} - -status_t BufferQueueLayer::updateFrameNumber() { - mPreviousFrameNumber = mCurrentFrameNumber; - mCurrentFrameNumber = mConsumer->getFrameNumber(); - return NO_ERROR; -} - -void BufferQueueLayer::setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) { - mFrameTimelineInfo = frameTimelineInfo; -} - -// ----------------------------------------------------------------------- -// Interface implementation for BufferLayerConsumer::ContentsChangedListener -// ----------------------------------------------------------------------- - -void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) { - const int32_t layerId = getSequence(); - mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER, - systemTime(), FrameTracer::FrameEvent::DEQUEUE); -} - -void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) { - const int32_t layerId = getSequence(); - mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER, - systemTime(), FrameTracer::FrameEvent::DETACH); -} - -void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) { - const int32_t layerId = getSequence(); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER, - systemTime(), FrameTracer::FrameEvent::CANCEL); -} - -void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { - const int32_t layerId = getSequence(); - const uint64_t bufferId = item.mGraphicBuffer->getId(); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(), - FrameTracer::FrameEvent::QUEUE); - mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber, - std::make_shared<FenceTime>(item.mFence), - FrameTracer::FrameEvent::ACQUIRE_FENCE); - - ATRACE_CALL(); - // Add this buffer from our internal queue tracker - { // Autolock scope - const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp; - - using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; - mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer); - - Mutex::Autolock lock(mQueueItemLock); - // Reset the frame number tracker when we receive the first buffer after - // a frame number reset - if (item.mFrameNumber == 1) { - mLastFrameNumberReceived = 0; - } - - // Ensure that callbacks are handled in order - while (item.mFrameNumber != mLastFrameNumberReceived + 1) { - status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); - if (result != NO_ERROR) { - ALOGE("[%s] Timed out waiting on callback", getDebugName()); - break; - } - } - - auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName); - - mQueueItems.push_back({item, surfaceFrame}); - mQueuedFrames++; - - // Wake up any pending callbacks - mLastFrameNumberReceived = item.mFrameNumber; - mQueueItemCondition.broadcast(); - } - - mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(), - item.mGraphicBuffer->getHeight(), item.mFrameNumber); - - mFlinger->onLayerUpdate(); - mConsumer->onBufferAvailable(item); -} - -void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { - ATRACE_CALL(); - { // Autolock scope - Mutex::Autolock lock(mQueueItemLock); - - // Ensure that callbacks are handled in order - while (item.mFrameNumber != mLastFrameNumberReceived + 1) { - status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); - if (result != NO_ERROR) { - ALOGE("[%s] Timed out waiting on callback", getDebugName()); - break; - } - } - - if (!hasFrameUpdate()) { - ALOGE("Can't replace a frame on an empty queue"); - return; - } - - auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName); - mQueueItems[mQueueItems.size() - 1].item = item; - mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); - - // Wake up any pending callbacks - mLastFrameNumberReceived = item.mFrameNumber; - mQueueItemCondition.broadcast(); - } - - const int32_t layerId = getSequence(); - const uint64_t bufferId = item.mGraphicBuffer->getId(); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(), - FrameTracer::FrameEvent::QUEUE); - mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber, - std::make_shared<FenceTime>(item.mFence), - FrameTracer::FrameEvent::ACQUIRE_FENCE); - mConsumer->onBufferAvailable(item); -} - -void BufferQueueLayer::onSidebandStreamChanged() { - bool sidebandStreamChanged = false; - if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) { - // mSidebandStreamChanged was changed to true - mFlinger->onLayerUpdate(); - } -} - -// ----------------------------------------------------------------------- - -void BufferQueueLayer::onFirstRef() { - BufferLayer::onFirstRef(); - - // Creates a custom BufferQueue for SurfaceFlingerConsumer to use - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - mFlinger->getFactory().createBufferQueue(&producer, &consumer, true); - mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this); - mConsumer = - mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(), - mTextureName, this); - mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); - - mContentsChangedListener = new ContentsChangedListener(this); - mConsumer->setContentsChangedListener(mContentsChangedListener); - mConsumer->setName(String8(mName.data(), mName.size())); - - mProducer->setMaxDequeuedBufferCount(2); -} - -status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) { - // never allow a surface larger than what our underlying GL implementation - // can handle. - if (mFlinger->exceedsMaxRenderTargetSize(w, h)) { - ALOGE("dimensions too large %" PRIu32 " x %" PRIu32, w, h); - return BAD_VALUE; - } - - setDefaultBufferSize(w, h); - mConsumer->setDefaultBufferFormat(format); - mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); - - return NO_ERROR; -} - -sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const { - return mProducer; -} - -uint32_t BufferQueueLayer::getProducerStickyTransform() const { - int producerStickyTransform = 0; - int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform); - if (ret != OK) { - ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__, - strerror(-ret), ret); - return 0; - } - return static_cast<uint32_t>(producerStickyTransform); -} - -void BufferQueueLayer::gatherBufferInfo() { - BufferLayer::gatherBufferInfo(); - - mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp(); - mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime(); - mBufferInfo.mFence = mConsumer->getCurrentFence(); - mBufferInfo.mTransform = mConsumer->getCurrentTransform(); - mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace()); - mBufferInfo.mCrop = mConsumer->getCurrentCrop(); - mBufferInfo.mScaleMode = mConsumer->getCurrentScalingMode(); - mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage(); - mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata(); - mBufferInfo.mApi = mConsumer->getCurrentApi(); - mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse(); -} - -sp<Layer> BufferQueueLayer::createClone() { - LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); - args.textureName = mTextureName; - sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args); - layer->setInitialValuesForClone(this); - - return layer; -} - -// ----------------------------------------------------------------------- -// Interface implementation for BufferLayerConsumer::ContentsChangedListener -// ----------------------------------------------------------------------- - -void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onFrameAvailable(item); - } -} - -void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onFrameReplaced(item); - } -} - -void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onSidebandStreamChanged(); - } -} - -void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onFrameDequeued(bufferId); - } -} - -void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onFrameDetached(bufferId); - } -} - -void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) { - Mutex::Autolock lock(mMutex); - if (mBufferQueueLayer != nullptr) { - mBufferQueueLayer->onFrameCancelled(bufferId); - } -} - -void BufferQueueLayer::ContentsChangedListener::abandon() { - Mutex::Autolock lock(mMutex); - mBufferQueueLayer = nullptr; -} - -// ----------------------------------------------------------------------- - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h deleted file mode 100644 index e1c80d581d..0000000000 --- a/services/surfaceflinger/BufferQueueLayer.h +++ /dev/null @@ -1,154 +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 "BufferLayer.h" - -#include <utils/String8.h> - -namespace android { - -namespace frametimeline { -class SurfaceFrame; -} - -/* - * A new BufferQueue and a new BufferLayerConsumer are created when the - * BufferLayer is first referenced. - * - * This also implements onFrameAvailable(), which notifies SurfaceFlinger - * that new data has arrived. - */ -class BufferQueueLayer : public BufferLayer { -public: - // Only call while mStateLock is held - explicit BufferQueueLayer(const LayerCreationArgs&); - ~BufferQueueLayer() override; - - // Implements Layer. - const char* getType() const override { return "BufferQueueLayer"; } - - void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; - - // If a buffer was replaced this frame, release the former buffer - void releasePendingBuffer(nsecs_t dequeueReadyTime) override; - - void setDefaultBufferSize(uint32_t w, uint32_t h) override; - - int32_t getQueuedFrameCount() const override; - - // Returns true if the next buffer should be presented at the expected present time - bool isBufferDue(nsecs_t expectedPresentTime) const override; - - // Implements BufferLayer. - bool fenceHasSignaled() const override; - bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; - - status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format); - sp<IGraphicBufferProducer> getProducer() const; - - void setSizeForTest(uint32_t w, uint32_t h) { - mDrawingState.active_legacy.w = w; - mDrawingState.active_legacy.h = h; - } - -protected: - void gatherBufferInfo() override; - - // ----------------------------------------------------------------------- - // Interface implementation for BufferLayerConsumer::ContentsChangedListener - // ----------------------------------------------------------------------- - class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener { - public: - ContentsChangedListener(BufferQueueLayer* bufferQueueLayer) - : mBufferQueueLayer(bufferQueueLayer) {} - void abandon(); - - protected: - void onFrameAvailable(const BufferItem& item) override; - void onFrameReplaced(const BufferItem& item) override; - void onSidebandStreamChanged() override; - void onFrameDequeued(const uint64_t bufferId) override; - void onFrameDetached(const uint64_t bufferId) override; - void onFrameCancelled(const uint64_t bufferId) override; - - private: - BufferQueueLayer* mBufferQueueLayer = nullptr; - Mutex mMutex; - }; - -private: - - bool latchSidebandStream(bool& recomputeVisibleRegions) override; - void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; - - bool hasFrameUpdate() const override; - - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) override; - - status_t updateActiveBuffer() override; - status_t updateFrameNumber() override; - void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) override; - - sp<Layer> createClone() override; - - void onFirstRef() override; - - void onFrameAvailable(const BufferItem& item); - void onFrameReplaced(const BufferItem& item); - void onSidebandStreamChanged(); - void onFrameDequeued(const uint64_t bufferId); - void onFrameDetached(const uint64_t bufferId); - void onFrameCancelled(const uint64_t bufferId); - - // Temporary - Used only for LEGACY camera mode. - uint32_t getProducerStickyTransform() const; - - sp<BufferLayerConsumer> mConsumer; - sp<IGraphicBufferProducer> mProducer; - - bool mUpdateTexImageFailed{false}; - - uint64_t mPreviousBufferId = 0; - uint64_t mPreviousReleasedFrameNumber = 0; - - // Local copy of the queued contents of the incoming BufferQueue - mutable Mutex mQueueItemLock; - Condition mQueueItemCondition; - - struct BufferData { - BufferData(BufferItem item, std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) - : item(item), surfaceFrame(surfaceFrame) {} - BufferItem item; - std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame; - }; - std::vector<BufferData> mQueueItems; - std::atomic<uint64_t> mLastFrameNumberReceived{0}; - - // thread-safe - std::atomic<int32_t> mQueuedFrames{0}; - - sp<ContentsChangedListener> mContentsChangedListener; - - // The last vsync info received on this layer. This will be used when we get - // a buffer to correlate the buffer with the vsync id. Can only be accessed - // with the SF state lock held. - FrameTimelineInfo mFrameTimelineInfo; -}; - -} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp deleted file mode 100644 index 3875f151cb..0000000000 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ /dev/null @@ -1,1085 +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 "BufferStateLayer" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "BufferStateLayer.h" - -#include <limits> - -#include <FrameTimeline/FrameTimeline.h> -#include <compositionengine/LayerFECompositionState.h> -#include <gui/BufferQueue.h> -#include <private/gui/SyncFeatures.h> -#include <renderengine/Image.h> -#include "TunnelModeEnabledReporter.h" - -#include "EffectLayer.h" -#include "FrameTracer/FrameTracer.h" -#include "TimeStats/TimeStats.h" - -#define EARLY_RELEASE_ENABLED false - -namespace android { - -using PresentState = frametimeline::SurfaceFrame::PresentState; -namespace { -void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, - const sp<GraphicBuffer>& buffer, uint64_t framenumber, - const sp<Fence>& releaseFence, - uint32_t currentMaxAcquiredBufferCount) { - if (!listener) { - return; - } - listener->onReleaseBuffer({buffer->getId(), framenumber}, - releaseFence ? releaseFence : Fence::NO_FENCE, - currentMaxAcquiredBufferCount); -} -} // namespace - -BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) - : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) { - mDrawingState.dataspace = ui::Dataspace::V0_SRGB; -} - -BufferStateLayer::~BufferStateLayer() { - // The original layer and the clone layer share the same texture and buffer. Therefore, only - // one of the layers, in this case the original layer, needs to handle the deletion. The - // original layer and the clone should be removed at the same time so there shouldn't be any - // issue with the clone layer trying to use the texture. - if (mBufferInfo.mBuffer != nullptr) { - callReleaseBufferCallback(mDrawingState.releaseBufferListener, - mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, - mBufferInfo.mFence, - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( - mOwnerUid)); - } -} - -// ----------------------------------------------------------------------- -// Interface implementation for Layer -// ----------------------------------------------------------------------- -void BufferStateLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { - // If we are displayed on multiple displays in a single composition cycle then we would - // need to do careful tracking to enable the use of the mLastClientCompositionFence. - // For example we can only use it if all the displays are client comp, and we need - // to merge all the client comp fences. We could do this, but for now we just - // disable the optimization when a layer is composed on multiple displays. - if (mClearClientCompositionFenceOnLayerDisplayed) { - mLastClientCompositionFence = nullptr; - } else { - mClearClientCompositionFenceOnLayerDisplayed = true; - } - - // The previous release fence notifies the client that SurfaceFlinger is done with the previous - // buffer that was presented on this layer. The first transaction that came in this frame that - // replaced the previous buffer on this layer needs this release fence, because the fence will - // let the client know when that previous buffer is removed from the screen. - // - // Every other transaction on this layer does not need a release fence because no other - // Transactions that were set on this layer this frame are going to have their preceeding buffer - // removed from the display this frame. - // - // For example, if we have 3 transactions this frame. The first transaction doesn't contain a - // buffer so it doesn't need a previous release fence because the layer still needs the previous - // buffer. The second transaction contains a buffer so it needs a previous release fence because - // the previous buffer will be released this frame. The third transaction also contains a - // buffer. It replaces the buffer in the second transaction. The buffer in the second - // transaction will now no longer be presented so it is released immediately and the third - // transaction doesn't need a previous release fence. - sp<CallbackHandle> ch; - for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer && - mDrawingState.releaseBufferEndpoint == handle->listener) { - ch = handle; - break; - } - } - - // Prevent tracing the same release multiple times. - if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) { - mPreviousReleasedFrameNumber = mPreviousFrameNumber; - } - - if (ch != nullptr) { - ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; - ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); - ch->name = mName; - } -} - -void BufferStateLayer::onSurfaceFrameCreated( - const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) { - while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) { - // Too many SurfaceFrames pending classification. The front of the deque is probably not - // tracked by FrameTimeline and will never be presented. This will only result in a memory - // leak. - ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak", - mName.c_str()); - std::string miniDump = mPendingJankClassifications.front()->miniDump(); - ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str()); - mPendingJankClassifications.pop_front(); - } - mPendingJankClassifications.emplace_back(surfaceFrame); -} - -void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { - for (const auto& handle : mDrawingState.callbackHandles) { - handle->transformHint = mTransformHint; - handle->dequeueReadyTime = dequeueReadyTime; - handle->currentMaxAcquiredBufferCount = - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); - } - - for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer && - mDrawingState.releaseBufferEndpoint == handle->listener) { - handle->previousReleaseCallbackId = mPreviousReleaseCallbackId; - break; - } - } - - std::vector<JankData> jankData; - jankData.reserve(mPendingJankClassifications.size()); - while (!mPendingJankClassifications.empty() - && mPendingJankClassifications.front()->getJankType()) { - std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame = - mPendingJankClassifications.front(); - mPendingJankClassifications.pop_front(); - jankData.emplace_back( - JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value())); - } - - mFlinger->getTransactionCallbackInvoker().addCallbackHandles( - mDrawingState.callbackHandles, jankData); - - sp<Fence> releaseFence = Fence::NO_FENCE; - for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer && - mDrawingState.releaseBufferEndpoint == handle->listener) { - releaseFence = - handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE; - break; - } - } - - mDrawingState.callbackHandles = {}; -} - -void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence, - const CompositorTiming& compositorTiming) { - for (const auto& handle : mDrawingState.callbackHandles) { - handle->gpuCompositionDoneFence = glDoneFence; - handle->compositorTiming = compositorTiming; - } -} - -bool BufferStateLayer::willPresentCurrentTransaction() const { - // Returns true if the most recent Transaction applied to CurrentState will be presented. - return (getSidebandStreamChanged() || getAutoRefresh() || - (mDrawingState.modified && - (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr))); -} - -Rect BufferStateLayer::getCrop(const Layer::State& s) const { - return s.crop; -} - -bool BufferStateLayer::setTransform(uint32_t transform) { - if (mDrawingState.bufferTransform == transform) return false; - mDrawingState.bufferTransform = transform; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) { - if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false; - mDrawingState.sequence++; - mDrawingState.transformToDisplayInverse = transformToDisplayInverse; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setCrop(const Rect& crop) { - if (mDrawingState.crop == crop) return false; - mDrawingState.sequence++; - mDrawingState.crop = crop; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) { - if (mDrawingState.bufferCrop == bufferCrop) return false; - - mDrawingState.sequence++; - mDrawingState.bufferCrop = bufferCrop; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) { - if (mDrawingState.destinationFrame == destinationFrame) return false; - - mDrawingState.sequence++; - mDrawingState.destinationFrame = destinationFrame; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -static bool assignTransform(ui::Transform* dst, ui::Transform& from) { - if (*dst == from) { - return false; - } - *dst = from; - return true; -} - -// Translate destination frame into scale and position. If a destination frame is not set, use the -// provided scale and position -bool BufferStateLayer::updateGeometry() { - if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) || - mDrawingState.destinationFrame.isEmpty()) { - // If destination frame is not set, use the requested transform set via - // BufferStateLayer::setPosition and BufferStateLayer::setMatrix. - return assignTransform(&mDrawingState.transform, mRequestedTransform); - } - - Rect destRect = mDrawingState.destinationFrame; - int32_t destW = destRect.width(); - int32_t destH = destRect.height(); - if (destRect.left < 0) { - destRect.left = 0; - destRect.right = destW; - } - if (destRect.top < 0) { - destRect.top = 0; - destRect.bottom = destH; - } - - if (!mDrawingState.buffer) { - ui::Transform t; - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); - } - - uint32_t bufferWidth = mDrawingState.buffer->getWidth(); - uint32_t bufferHeight = mDrawingState.buffer->getHeight(); - // Undo any transformations on the buffer. - if (mDrawingState.bufferTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (mDrawingState.transformToDisplayInverse) { - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - } - - float sx = destW / static_cast<float>(bufferWidth); - float sy = destH / static_cast<float>(bufferHeight); - ui::Transform t; - t.set(sx, 0, 0, sy); - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); -} - -bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix) { - if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && - mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { - return false; - } - - ui::Transform t; - t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - - mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool BufferStateLayer::setPosition(float x, float y) { - if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) { - return false; - } - - mRequestedTransform.set(x, y); - - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool BufferStateLayer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, - const BufferData& bufferData, nsecs_t postTime, - nsecs_t desiredPresentTime, bool isAutoTimestamp, - std::optional<nsecs_t> dequeueTime, - const FrameTimelineInfo& info) { - ATRACE_CALL(); - - if (!buffer) { - return false; - } - - const bool frameNumberChanged = - bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged); - const uint64_t frameNumber = - frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1; - - if (mDrawingState.buffer) { - mReleasePreviousBuffer = true; - if (!mBufferInfo.mBuffer || - (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) || - mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) { - // If mDrawingState has a buffer, and we are about to update again - // before swapping to drawing state, then the first buffer will be - // dropped and we should decrement the pending buffer count and - // call any release buffer callbacks if set. - callReleaseBufferCallback(mDrawingState.releaseBufferListener, - mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, - mDrawingState.acquireFence, - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( - mOwnerUid)); - decrementPendingBufferCount(); - if (mDrawingState.bufferSurfaceFrameTX != nullptr && - mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) { - addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX); - mDrawingState.bufferSurfaceFrameTX.reset(); - } - } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) { - callReleaseBufferCallback(mDrawingState.releaseBufferListener, - mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, - mLastClientCompositionFence, - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( - mOwnerUid)); - mLastClientCompositionFence = nullptr; - } - } - - mDrawingState.frameNumber = frameNumber; - mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; - mDrawingState.buffer = std::move(buffer); - mDrawingState.clientCacheId = bufferData.cachedBuffer; - - mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) - ? bufferData.acquireFence - : Fence::NO_FENCE; - mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence); - if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { - // We latched this buffer unsiganled, so we need to pass the acquire fence - // on the callback instead of just the acquire time, since it's unknown at - // this point. - mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence; - } else { - mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime(); - } - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - const int32_t layerId = getSequence(); - mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(), - mOwnerUid, postTime, getGameMode()); - mDrawingState.desiredPresentTime = desiredPresentTime; - mDrawingState.isAutoTimestamp = isAutoTimestamp; - - const nsecs_t presentTime = [&] { - if (!isAutoTimestamp) return desiredPresentTime; - - const auto prediction = - mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId); - if (prediction.has_value()) return prediction->presentTime; - - return static_cast<nsecs_t>(0); - }(); - - using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; - mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer); - - setFrameTimelineVsyncForBufferTransaction(info, postTime); - - if (dequeueTime && *dequeueTime != 0) { - const uint64_t bufferId = mDrawingState.buffer->getId(); - mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime, - FrameTracer::FrameEvent::DEQUEUE); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime, - FrameTracer::FrameEvent::QUEUE); - } - - mDrawingState.width = mDrawingState.buffer->getWidth(); - mDrawingState.height = mDrawingState.buffer->getHeight(); - mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; - return true; -} - -bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) { - if (mDrawingState.dataspace == dataspace) return false; - mDrawingState.dataspace = dataspace; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) { - if (mDrawingState.hdrMetadata == hdrMetadata) return false; - mDrawingState.hdrMetadata = hdrMetadata; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) { - mDrawingState.surfaceDamageRegion = surfaceDamage; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setApi(int32_t api) { - if (mDrawingState.api == api) return false; - mDrawingState.api = api; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) { - if (mDrawingState.sidebandStream == sidebandStream) return false; - - if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) { - mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount(); - } else if (sidebandStream != nullptr) { - mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount(); - } - - mDrawingState.sidebandStream = sidebandStream; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - if (!mSidebandStreamChanged.exchange(true)) { - // mSidebandStreamChanged was false - mFlinger->onLayerUpdate(); - } - return true; -} - -bool BufferStateLayer::setTransactionCompletedListeners( - const std::vector<sp<CallbackHandle>>& handles) { - // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return - if (handles.empty()) { - mReleasePreviousBuffer = false; - return false; - } - - const bool willPresent = willPresentCurrentTransaction(); - - for (const auto& handle : handles) { - // If this transaction set a buffer on this layer, release its previous buffer - handle->releasePreviousBuffer = mReleasePreviousBuffer; - - // If this layer will be presented in this frame - if (willPresent) { - // If this transaction set an acquire fence on this layer, set its acquire time - handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; - handle->frameNumber = mDrawingState.frameNumber; - - // Store so latched time and release fence can be set - mDrawingState.callbackHandles.push_back(handle); - - } else { // If this layer will NOT need to be relatched and presented this frame - // Notify the transaction completed thread this handle is done - mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle); - } - } - - mReleasePreviousBuffer = false; - mCallbackHandleAcquireTimeOrFence = -1; - - return willPresent; -} - -bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { - mDrawingState.sequence++; - mDrawingState.transparentRegionHint = transparent; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -Rect BufferStateLayer::getBufferSize(const State& /*s*/) const { - // for buffer state layers we use the display frame size as the buffer size. - - if (mBufferInfo.mBuffer == nullptr) { - return Rect::INVALID_RECT; - } - - uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); - uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); - - // Undo any transformations on the buffer and return the result. - if (mBufferInfo.mTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - - if (getTransformToDisplayInverse()) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - } - - return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); -} - -FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const { - if (mBufferInfo.mBuffer == nullptr) { - return parentBounds; - } - - return getBufferSize(getDrawingState()).toFloatRect(); -} - -// ----------------------------------------------------------------------- - -// ----------------------------------------------------------------------- -// Interface implementation for BufferLayer -// ----------------------------------------------------------------------- -bool BufferStateLayer::fenceHasSignaled() const { - if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { - return true; - } - - const bool fenceSignaled = - getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; - if (!fenceSignaled) { - mFlinger->mTimeStats->incrementLatchSkipped(getSequence(), - TimeStats::LatchSkipReason::LateAcquire); - } - - return fenceSignaled; -} - -bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { - if (!hasFrameUpdate() || isRemovedFromCurrentState()) { - return true; - } - - return mDrawingState.isAutoTimestamp || mDrawingState.desiredPresentTime <= expectedPresentTime; -} - -bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) { - for (const auto& handle : mDrawingState.callbackHandles) { - handle->refreshStartTime = refreshStartTime; - } - return BufferLayer::onPreComposition(refreshStartTime); -} - -void BufferStateLayer::setAutoRefresh(bool autoRefresh) { - mDrawingState.autoRefresh = autoRefresh; -} - -bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { - // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); - - if (mSidebandStreamChanged.exchange(false)) { - const State& s(getDrawingState()); - // mSidebandStreamChanged was true - mSidebandStream = s.sidebandStream; - editCompositionState()->sidebandStream = mSidebandStream; - if (mSidebandStream != nullptr) { - setTransactionFlags(eTransactionNeeded); - mFlinger->setTransactionFlags(eTraversalNeeded); - } - recomputeVisibleRegions = true; - - return true; - } - return false; -} - -bool BufferStateLayer::hasFrameUpdate() const { - const State& c(getDrawingState()); - return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr); -} - -status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, - nsecs_t /*expectedPresentTime*/) { - const State& s(getDrawingState()); - - if (!s.buffer) { - if (s.bgColorLayer) { - for (auto& handle : mDrawingState.callbackHandles) { - handle->latchTime = latchTime; - } - } - return NO_ERROR; - } - - for (auto& handle : mDrawingState.callbackHandles) { - if (handle->frameNumber == mDrawingState.frameNumber) { - handle->latchTime = latchTime; - } - } - - const int32_t layerId = getSequence(); - const uint64_t bufferId = mDrawingState.buffer->getId(); - const uint64_t frameNumber = mDrawingState.frameNumber; - const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence); - mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence); - mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime); - - mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence, - FrameTracer::FrameEvent::ACQUIRE_FENCE); - mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime, - FrameTracer::FrameEvent::LATCH); - - auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX; - if (bufferSurfaceFrame != nullptr && - bufferSurfaceFrame->getPresentState() != PresentState::Presented) { - // Update only if the bufferSurfaceFrame wasn't already presented. A Presented - // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we - // are processing the next state. - addSurfaceFramePresentedForBuffer(bufferSurfaceFrame, - mDrawingState.acquireFenceTime->getSignalTime(), - latchTime); - mDrawingState.bufferSurfaceFrameTX.reset(); - } - - std::deque<sp<CallbackHandle>> remainingHandles; - mFlinger->getTransactionCallbackInvoker() - .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); - mDrawingState.callbackHandles = remainingHandles; - - mDrawingStateModified = false; - - return NO_ERROR; -} - -status_t BufferStateLayer::updateActiveBuffer() { - const State& s(getDrawingState()); - - if (s.buffer == nullptr) { - return BAD_VALUE; - } - - if (!mBufferInfo.mBuffer || !s.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) { - decrementPendingBufferCount(); - } - - mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber}; - mBufferInfo.mBuffer = s.buffer; - mBufferInfo.mFence = s.acquireFence; - mBufferInfo.mFrameNumber = s.frameNumber; - - return NO_ERROR; -} - -status_t BufferStateLayer::updateFrameNumber() { - // TODO(marissaw): support frame history events - mPreviousFrameNumber = mCurrentFrameNumber; - mCurrentFrameNumber = mDrawingState.frameNumber; - return NO_ERROR; -} - -void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) { - std::lock_guard lock(mMutex); - if (!clientCacheId.isValid()) { - ALOGE("invalid process, failed to erase buffer"); - return; - } - eraseBufferLocked(clientCacheId); -} - -int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) { - std::lock_guard<std::mutex> lock(mMutex); - auto itr = mCachedBuffers.find(clientCacheId); - if (itr == mCachedBuffers.end()) { - return addCachedBuffer(clientCacheId); - } - auto& [hwcCacheSlot, counter] = itr->second; - counter = mCounter++; - return hwcCacheSlot; -} - -int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) - REQUIRES(mMutex) { - if (!clientCacheId.isValid()) { - ALOGE("invalid process, returning invalid slot"); - return BufferQueue::INVALID_BUFFER_SLOT; - } - - ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this)); - - int hwcCacheSlot = getFreeHwcCacheSlot(); - mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++}; - return hwcCacheSlot; -} - -int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) { - if (mFreeHwcCacheSlots.empty()) { - evictLeastRecentlyUsed(); - } - - int hwcCacheSlot = mFreeHwcCacheSlots.top(); - mFreeHwcCacheSlots.pop(); - return hwcCacheSlot; -} - -void BufferStateLayer::HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) { - uint64_t minCounter = UINT_MAX; - client_cache_t minClientCacheId = {}; - for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) { - const auto& [hwcCacheSlot, counter] = slotCounter; - if (counter < minCounter) { - minCounter = counter; - minClientCacheId = clientCacheId; - } - } - eraseBufferLocked(minClientCacheId); - - ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this); -} - -void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) - REQUIRES(mMutex) { - auto itr = mCachedBuffers.find(clientCacheId); - if (itr == mCachedBuffers.end()) { - return; - } - auto& [hwcCacheSlot, counter] = itr->second; - - // TODO send to hwc cache and resources - - mFreeHwcCacheSlots.push(hwcCacheSlot); - mCachedBuffers.erase(clientCacheId); -} - -void BufferStateLayer::gatherBufferInfo() { - BufferLayer::gatherBufferInfo(); - - const State& s(getDrawingState()); - mBufferInfo.mDesiredPresentTime = s.desiredPresentTime; - mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence); - mBufferInfo.mFence = s.acquireFence; - mBufferInfo.mTransform = s.bufferTransform; - auto lastDataspace = mBufferInfo.mDataspace; - mBufferInfo.mDataspace = translateDataspace(s.dataspace); - if (lastDataspace != mBufferInfo.mDataspace) { - mFlinger->mSomeDataspaceChanged = true; - } - mBufferInfo.mCrop = computeBufferCrop(s); - mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; - mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion; - mBufferInfo.mHdrMetadata = s.hdrMetadata; - mBufferInfo.mApi = s.api; - mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse; - mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId); -} - -uint32_t BufferStateLayer::getEffectiveScalingMode() const { - return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; -} - -Rect BufferStateLayer::computeBufferCrop(const State& s) { - if (s.buffer && !s.bufferCrop.isEmpty()) { - Rect bufferCrop; - s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop); - return bufferCrop; - } else if (s.buffer) { - return s.buffer->getBounds(); - } else { - return s.bufferCrop; - } -} - -sp<Layer> BufferStateLayer::createClone() { - LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); - args.textureName = mTextureName; - sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args); - layer->mHwcSlotGenerator = mHwcSlotGenerator; - layer->setInitialValuesForClone(this); - return layer; -} - -bool BufferStateLayer::bufferNeedsFiltering() const { - const State& s(getDrawingState()); - if (!s.buffer) { - return false; - } - - int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth()); - int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight()); - - // Undo any transformations on the buffer and return the result. - if (s.bufferTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - - if (s.transformToDisplayInverse) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - } - - const Rect layerSize{getBounds()}; - return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight; -} - -void BufferStateLayer::decrementPendingBufferCount() { - int32_t pendingBuffers = --mPendingBufferTransactions; - tracePendingBufferCount(pendingBuffers); -} - -void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) { - ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); -} - - -/* - * We don't want to send the layer's transform to input, but rather the - * parent's transform. This is because BufferStateLayer's transform is - * information about how the buffer is placed on screen. The parent's - * transform makes more sense to send since it's information about how the - * layer is placed on screen. This transform is used by input to determine - * how to go from screen space back to window space. - */ -ui::Transform BufferStateLayer::getInputTransform() const { - sp<Layer> parent = mDrawingParent.promote(); - if (parent == nullptr) { - return ui::Transform(); - } - - return parent->getTransform(); -} - -/** - * Similar to getInputTransform, we need to update the bounds to include the transform. - * This is because bounds for BSL doesn't include buffer transform, where the input assumes - * that's already included. - */ -Rect BufferStateLayer::getInputBounds() const { - Rect bufferBounds = getCroppedBufferSize(getDrawingState()); - if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) { - return bufferBounds; - } - return mDrawingState.transform.transform(bufferBounds); -} - -bool BufferStateLayer::simpleBufferUpdate(const layer_state_t& s) const { - const uint64_t requiredFlags = layer_state_t::eBufferChanged; - - const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged | - layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged | - layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged | - layer_state_t::eReparent; - - const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged | - layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged | - layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged | - layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged | - layer_state_t::eInputInfoChanged; - - if ((s.what & requiredFlags) != requiredFlags) { - ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__, - (s.what | requiredFlags) & ~s.what); - return false; - } - - if (s.what & deniedFlags) { - ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags); - return false; - } - - if (s.what & allowedFlags) { - ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags); - } - - if (s.what & layer_state_t::ePositionChanged) { - if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) { - ALOGV("%s: false [ePositionChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eAlphaChanged) { - if (mDrawingState.color.a != s.alpha) { - ALOGV("%s: false [eAlphaChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorTransformChanged) { - if (mDrawingState.colorTransform != s.colorTransform) { - ALOGV("%s: false [eColorTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundColorChanged) { - if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) { - ALOGV("%s: false [eBackgroundColorChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eMatrixChanged) { - if (mRequestedTransform.dsdx() != s.matrix.dsdx || - mRequestedTransform.dtdy() != s.matrix.dtdy || - mRequestedTransform.dtdx() != s.matrix.dtdx || - mRequestedTransform.dsdy() != s.matrix.dsdy) { - ALOGV("%s: false [eMatrixChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eCornerRadiusChanged) { - if (mDrawingState.cornerRadius != s.cornerRadius) { - ALOGV("%s: false [eCornerRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) { - if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) { - ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTransformChanged) { - if (mDrawingState.bufferTransform != s.transform) { - ALOGV("%s: false [eTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTransformToDisplayInverseChanged) { - if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) { - ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eCropChanged) { - if (mDrawingState.crop != s.crop) { - ALOGV("%s: false [eCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDataspaceChanged) { - if (mDrawingState.dataspace != s.dataspace) { - ALOGV("%s: false [eDataspaceChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eHdrMetadataChanged) { - if (mDrawingState.hdrMetadata != s.hdrMetadata) { - ALOGV("%s: false [eHdrMetadataChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eSidebandStreamChanged) { - if (mDrawingState.sidebandStream != s.sidebandStream) { - ALOGV("%s: false [eSidebandStreamChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorSpaceAgnosticChanged) { - if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) { - ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eShadowRadiusChanged) { - if (mDrawingState.shadowRadius != s.shadowRadius) { - ALOGV("%s: false [eShadowRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eFixedTransformHintChanged) { - if (mDrawingState.fixedTransformHint != s.fixedTransformHint) { - ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTrustedOverlayChanged) { - if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) { - ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eStretchChanged) { - StretchEffect temp = s.stretchEffect; - temp.sanitize(); - if (mDrawingState.stretchEffect != temp) { - ALOGV("%s: false [eStretchChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBufferCropChanged) { - if (mDrawingState.bufferCrop != s.bufferCrop) { - ALOGV("%s: false [eBufferCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDestinationFrameChanged) { - if (mDrawingState.destinationFrame != s.destinationFrame) { - ALOGV("%s: false [eDestinationFrameChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDimmingEnabledChanged) { - if (mDrawingState.dimmingEnabled != s.dimmingEnabled) { - ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__); - return false; - } - } - - ALOGV("%s: true", __func__); - return true; -} - -} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h deleted file mode 100644 index 3f0dbe4039..0000000000 --- a/services/surfaceflinger/BufferStateLayer.h +++ /dev/null @@ -1,211 +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 "BufferLayer.h" -#include "Layer.h" - -#include <renderengine/Image.h> -#include <renderengine/RenderEngine.h> -#include <system/window.h> -#include <utils/String8.h> - -#include <stack> - -namespace android { - -class SlotGenerationTest; - -class BufferStateLayer : public BufferLayer { -public: - explicit BufferStateLayer(const LayerCreationArgs&); - - ~BufferStateLayer() override; - - // Implements Layer. - const char* getType() const override { return "BufferStateLayer"; } - - void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; - - void releasePendingBuffer(nsecs_t dequeueReadyTime) override; - - void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence, - const CompositorTiming& compositorTiming) override; - - bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; } - - Region getActiveTransparentRegion(const Layer::State& s) const override { - return s.transparentRegionHint; - } - Rect getCrop(const Layer::State& s) const; - - bool setTransform(uint32_t transform) override; - bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; - bool setCrop(const Rect& crop) override; - bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, - const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, - bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime, - const FrameTimelineInfo& info) override; - bool setDataspace(ui::Dataspace dataspace) override; - bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; - bool setSurfaceDamageRegion(const Region& surfaceDamage) override; - bool setApi(int32_t api) override; - bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override; - bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override; - bool setPosition(float /*x*/, float /*y*/) override; - bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/); - - // Override to ignore legacy layer state properties that are not used by BufferStateLayer - bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; } - bool setTransparentRegionHint(const Region& transparent) override; - - Rect getBufferSize(const State& s) const override; - FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; - void setAutoRefresh(bool autoRefresh) override; - - bool setBufferCrop(const Rect& bufferCrop) override; - bool setDestinationFrame(const Rect& destinationFrame) override; - bool updateGeometry() override; - - // ----------------------------------------------------------------------- - - // ----------------------------------------------------------------------- - // Interface implementation for BufferLayer - // ----------------------------------------------------------------------- - bool fenceHasSignaled() const override; - bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; - bool onPreComposition(nsecs_t refreshStartTime) override; - uint32_t getEffectiveScalingMode() const override; - - // See mPendingBufferTransactions - void decrementPendingBufferCount(); - std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; } - std::string getPendingBufferCounterName() override { return mBlastTransactionName; } - - bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; } - -protected: - void gatherBufferInfo() override; - void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame); - ui::Transform getInputTransform() const override; - Rect getInputBounds() const override; - -private: - friend class SlotGenerationTest; - friend class TransactionFrameTracerTest; - friend class TransactionSurfaceFrameTest; - - inline void tracePendingBufferCount(int32_t pendingBuffers); - - bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime, - nsecs_t requestedPresentTime); - - bool latchSidebandStream(bool& recomputeVisibleRegions) override; - - bool hasFrameUpdate() const override; - - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) override; - - status_t updateActiveBuffer() override; - status_t updateFrameNumber() override; - - sp<Layer> createClone() override; - - // Crop that applies to the buffer - Rect computeBufferCrop(const State& s); - - bool willPresentCurrentTransaction() const; - - bool bufferNeedsFiltering() const override; - - bool simpleBufferUpdate(const layer_state_t& s) const override; - - ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; - uint64_t mPreviousReleasedFrameNumber = 0; - - uint64_t mPreviousBarrierFrameNumber = 0; - - bool mReleasePreviousBuffer = false; - - // Stores the last set acquire fence signal time used to populate the callback handle's acquire - // time. - std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1; - - std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications; - // An upper bound on the number of SurfaceFrames in the pending classifications deque. - static constexpr int kPendingClassificationMaxSurfaceFrames = 25; - - const std::string mBlastTransactionName{"BufferTX - " + mName}; - // This integer is incremented everytime a buffer arrives at the server for this layer, - // and decremented when a buffer is dropped or latched. When changed the integer is exported - // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is - // possible to see when a buffer arrived at the server, and in which frame it latched. - // - // You can understand the trace this way: - // - If the integer increases, a buffer arrived at the server. - // - If the integer decreases in latchBuffer, that buffer was latched - // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped - std::atomic<int32_t> mPendingBufferTransactions{0}; - - // Contains requested position and matrix updates. This will be applied if the client does - // not specify a destination frame. - ui::Transform mRequestedTransform; - - // TODO(marissaw): support sticky transform for LEGACY camera mode - - class HwcSlotGenerator : public ClientCache::ErasedRecipient { - public: - HwcSlotGenerator() { - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - mFreeHwcCacheSlots.push(i); - } - } - - void bufferErased(const client_cache_t& clientCacheId); - - int getHwcCacheSlot(const client_cache_t& clientCacheId); - - private: - friend class SlotGenerationTest; - int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex); - int getFreeHwcCacheSlot() REQUIRES(mMutex); - void evictLeastRecentlyUsed() REQUIRES(mMutex); - void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex); - - struct CachedBufferHash { - std::size_t operator()(const client_cache_t& clientCacheId) const { - return std::hash<uint64_t>{}(clientCacheId.id); - } - }; - - std::mutex mMutex; - - std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>, - CachedBufferHash> - mCachedBuffers GUARDED_BY(mMutex); - std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex); - - // The cache increments this counter value when a slot is updated or used. - // Used to track the least recently-used buffer - uint64_t mCounter = 0; - }; - - sp<HwcSlotGenerator> mHwcSlotGenerator; -}; - -} // namespace android diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 6d7b732b36..bdbc79b8e1 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -21,12 +21,18 @@ #include <private/android_filesystem_config.h> +#include <gui/AidlStatusUtil.h> + #include "Client.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "SurfaceFlinger.h" namespace android { +using gui::aidl_utils::binderStatusFromStatusT; + // --------------------------------------------------------------------------- const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"); @@ -42,82 +48,73 @@ status_t Client::initCheck() const { return NO_ERROR; } -void Client::attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer) -{ - Mutex::Autolock _l(mLock); - mLayers.add(handle, layer); -} - -void Client::detachLayer(const Layer* layer) -{ - Mutex::Autolock _l(mLock); - // we do a linear search here, because this doesn't happen often - const size_t count = mLayers.size(); - for (size_t i=0 ; i<count ; i++) { - if (mLayers.valueAt(i) == layer) { - mLayers.removeItemsAt(i, 1); - break; - } - } -} -sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const -{ - Mutex::Autolock _l(mLock); - sp<Layer> lbc; - wp<Layer> layer(mLayers.valueFor(handle)); - if (layer != 0) { - lbc = layer.promote(); - ALOGE_IF(lbc==0, "getLayerUser(name=%p) is dead", handle.get()); - } - return lbc; -} - -status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */, - PixelFormat /* format */, uint32_t flags, - const sp<IBinder>& parentHandle, LayerMetadata metadata, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */, - int32_t* outLayerId, uint32_t* outTransformHint) { +binder::Status Client::createSurface(const std::string& name, int32_t flags, + const sp<IBinder>& parent, const gui::LayerMetadata& metadata, + gui::CreateSurfaceResult* outResult) { // We rely on createLayer to check permissions. - LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata)); - return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr, - outTransformHint); + sp<IBinder> handle; + LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(), + static_cast<uint32_t>(flags), std::move(metadata)); + args.parentHandle = parent; + const status_t status = mFlinger->createLayer(args, *outResult); + return binderStatusFromStatusT(status); } -status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */, - uint32_t /* h */, PixelFormat /* format */, - uint32_t /* flags */, - const sp<IGraphicBufferProducer>& /* parent */, - LayerMetadata /* metadata */, sp<IBinder>* /* handle */, - sp<IGraphicBufferProducer>* /* gbp */, - int32_t* /* outLayerId */, - uint32_t* /* outTransformHint */) { - // This api does not make sense with blast since SF no longer tracks IGBP. This api should be - // removed. - return BAD_VALUE; -} - -status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, - int32_t* outLayerId) { - LayerCreationArgs args(mFlinger.get(), this, "MirrorRoot", 0 /* flags */, LayerMetadata()); - return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId); -} - -status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const { - sp<Layer> layer = getLayerUser(handle); +binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) { + status_t status; + sp<Layer> layer = LayerHandle::getLayer(handle); if (layer == nullptr) { - return NAME_NOT_FOUND; + status = NAME_NOT_FOUND; + } else { + layer->clearFrameStats(); + status = NO_ERROR; } - layer->clearFrameStats(); - return NO_ERROR; + return binderStatusFromStatusT(status); } -status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const { - sp<Layer> layer = getLayerUser(handle); +binder::Status Client::getLayerFrameStats(const sp<IBinder>& handle, gui::FrameStats* outStats) { + status_t status; + sp<Layer> layer = LayerHandle::getLayer(handle); if (layer == nullptr) { - return NAME_NOT_FOUND; + status = NAME_NOT_FOUND; + } else { + FrameStats stats; + layer->getFrameStats(&stats); + outStats->refreshPeriodNano = stats.refreshPeriodNano; + outStats->desiredPresentTimesNano.reserve(stats.desiredPresentTimesNano.size()); + for (const auto& t : stats.desiredPresentTimesNano) { + outStats->desiredPresentTimesNano.push_back(t); + } + outStats->actualPresentTimesNano.reserve(stats.actualPresentTimesNano.size()); + for (const auto& t : stats.actualPresentTimesNano) { + outStats->actualPresentTimesNano.push_back(t); + } + outStats->frameReadyTimesNano.reserve(stats.frameReadyTimesNano.size()); + for (const auto& t : stats.frameReadyTimesNano) { + outStats->frameReadyTimesNano.push_back(t); + } + status = NO_ERROR; } - layer->getFrameStats(outStats); - return NO_ERROR; + return binderStatusFromStatusT(status); +} + +binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, + gui::CreateSurfaceResult* outResult) { + sp<IBinder> handle; + LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot", + 0 /* flags */, gui::LayerMetadata()); + status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult); + return binderStatusFromStatusT(status); +} + +binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) { + sp<IBinder> handle; + LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), + "MirrorRoot-" + std::to_string(displayId), 0 /* flags */, + gui::LayerMetadata()); + std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId)); + status_t status = mFlinger->mirrorDisplay(*id, args, *outResult); + return binderStatusFromStatusT(status); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index 15cd763822..af410ea19c 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -24,55 +24,40 @@ #include <utils/KeyedVector.h> #include <utils/Mutex.h> -#include <gui/ISurfaceComposerClient.h> +#include <android/gui/BnSurfaceComposerClient.h> namespace android { class Layer; class SurfaceFlinger; -class Client : public BnSurfaceComposerClient -{ +class Client : public gui::BnSurfaceComposerClient { public: explicit Client(const sp<SurfaceFlinger>& flinger); ~Client() = default; status_t initCheck() const; - // protected by SurfaceFlinger::mStateLock - void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer); - void detachLayer(const Layer* layer); - - sp<Layer> getLayerUser(const sp<IBinder>& handle) const; - private: // ISurfaceComposerClient interface - virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags, const sp<IBinder>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint = nullptr); - virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, - PixelFormat format, uint32_t flags, - const sp<IGraphicBufferProducer>& parent, - LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, - uint32_t* outTransformHint = nullptr); + binder::Status createSurface(const std::string& name, int32_t flags, const sp<IBinder>& parent, + const gui::LayerMetadata& metadata, + gui::CreateSurfaceResult* outResult) override; - status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle, - int32_t* outLayerId); + binder::Status clearLayerFrameStats(const sp<IBinder>& handle) override; - virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const; + binder::Status getLayerFrameStats(const sp<IBinder>& handle, + gui::FrameStats* outStats) override; - virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const; + binder::Status mirrorSurface(const sp<IBinder>& mirrorFromHandle, + gui::CreateSurfaceResult* outResult) override; + + binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override; // constant sp<SurfaceFlinger> mFlinger; - // protected by mLock - DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers; - // thread-safe mutable Mutex mLock; }; diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index 3c7b9d93aa..09e41ffede 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -22,6 +22,7 @@ #include <cinttypes> #include <android-base/stringprintf.h> +#include <gui/TraceUtils.h> #include <renderengine/impl/ExternalTexture.h> #include "ClientCache.h" @@ -30,18 +31,18 @@ namespace android { ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache); -ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {} +ClientCache::ClientCache() : mDeathRecipient(sp<CacheDeathRecipient>::make()) {} bool ClientCache::getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer) { auto& [processToken, id] = cacheId; if (processToken == nullptr) { - ALOGE("failed to get buffer, invalid (nullptr) process token"); + ALOGE_AND_TRACE("ClientCache::getBuffer - invalid (nullptr) process token"); return false; } auto it = mBuffers.find(processToken); if (it == mBuffers.end()) { - ALOGE("failed to get buffer, invalid process token"); + ALOGE_AND_TRACE("ClientCache::getBuffer - invalid process token"); return false; } @@ -49,7 +50,7 @@ bool ClientCache::getBuffer(const client_cache_t& cacheId, auto bufItr = processBuffers.find(id); if (bufItr == processBuffers.end()) { - ALOGV("failed to get buffer, invalid buffer id"); + ALOGE_AND_TRACE("ClientCache::getBuffer - invalid buffer id"); return false; } @@ -58,16 +59,17 @@ bool ClientCache::getBuffer(const client_cache_t& cacheId, return true; } -bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) { +base::expected<std::shared_ptr<renderengine::ExternalTexture>, ClientCache::AddError> +ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) { auto& [processToken, id] = cacheId; if (processToken == nullptr) { - ALOGE("failed to cache buffer: invalid process token"); - return false; + ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token"); + return base::unexpected(AddError::Unspecified); } if (!buffer) { - ALOGE("failed to cache buffer: invalid buffer"); - return false; + ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer"); + return base::unexpected(AddError::Unspecified); } std::lock_guard lock(mMutex); @@ -79,16 +81,16 @@ bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& bu if (it == mBuffers.end()) { token = processToken.promote(); if (!token) { - ALOGE("failed to cache buffer: invalid token"); - return false; + ALOGE_AND_TRACE("ClientCache::add - invalid token"); + return base::unexpected(AddError::Unspecified); } // Only call linkToDeath if not a local binder if (token->localBinder() == nullptr) { status_t err = token->linkToDeath(mDeathRecipient); if (err != NO_ERROR) { - ALOGE("failed to cache buffer: could not link to death"); - return false; + ALOGE_AND_TRACE("ClientCache::add - could not link to death"); + return base::unexpected(AddError::Unspecified); } } auto [itr, success] = @@ -102,21 +104,22 @@ bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& bu auto& processBuffers = it->second.second; if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) { - ALOGE("failed to cache buffer: cache is full"); - return false; + ALOGE_AND_TRACE("ClientCache::add - cache is full"); + return base::unexpected(AddError::CacheFull); } LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr, "Attempted to build the ClientCache before a RenderEngine instance was " "ready!"); - processBuffers[id].buffer = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, *mRenderEngine, - renderengine::impl::ExternalTexture::Usage:: - READABLE); - return true; + + return (processBuffers[id].buffer = std::make_shared< + renderengine::impl::ExternalTexture>(buffer, *mRenderEngine, + renderengine::impl::ExternalTexture:: + Usage::READABLE)); } -void ClientCache::erase(const client_cache_t& cacheId) { +sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) { + sp<GraphicBuffer> buffer; auto& [processToken, id] = cacheId; std::vector<sp<ErasedRecipient>> pendingErase; { @@ -124,9 +127,11 @@ void ClientCache::erase(const client_cache_t& cacheId) { ClientCacheBuffer* buf = nullptr; if (!getBuffer(cacheId, &buf)) { ALOGE("failed to erase buffer, could not retrieve buffer"); - return; + return nullptr; } + buffer = buf->buffer->getBuffer(); + for (auto& recipient : buf->recipients) { sp<ErasedRecipient> erasedRecipient = recipient.promote(); if (erasedRecipient) { @@ -140,6 +145,7 @@ void ClientCache::erase(const client_cache_t& cacheId) { for (auto& recipient : pendingErase) { recipient->bufferErased(cacheId); } + return buffer; } std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) { diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h index a9b8177d70..b56b252d9f 100644 --- a/services/surfaceflinger/ClientCache.h +++ b/services/surfaceflinger/ClientCache.h @@ -33,12 +33,27 @@ namespace android { +// This class manages a cache of buffer handles between SurfaceFlinger clients +// and the SurfaceFlinger process which optimizes away some of the cost of +// sending buffer handles across processes. +// +// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When +// a buffer is uncached, it is not only purged from this cache, but the buffer +// ID is also passed down to CompositionEngine to purge it from a similar cache +// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge +// both the SurfaceFlinger side of this other cache, as well as Composer HAL's +// side of the cache. +// class ClientCache : public Singleton<ClientCache> { public: ClientCache(); - bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer); - void erase(const client_cache_t& cacheId); + enum class AddError { CacheFull, Unspecified }; + + base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add( + const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer); + + sp<GraphicBuffer> erase(const client_cache_t& cacheId); std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId); diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 11a9e19db8..30d34a581b 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -9,7 +9,10 @@ package { cc_defaults { name: "libcompositionengine_defaults", - defaults: ["surfaceflinger_defaults"], + defaults: [ + "android.hardware.graphics.composer3-ndk_shared", + "surfaceflinger_defaults", + ], cflags: [ "-DLOG_TAG=\"CompositionEngine\"", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", @@ -20,10 +23,9 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libbase", "libcutils", "libgui", @@ -40,7 +42,6 @@ cc_defaults { "libmath", "librenderengine", "libtonemap", - "libtrace_proto", "libaidlcommonsupport", "libprocessgroup", "libcgrouprc", @@ -139,6 +140,11 @@ cc_test { "libgmock", "libgtest", ], + // For some reason, libvulkan isn't picked up from librenderengine + // Probably ASAN related? + shared_libs: [ + "libvulkan", + ], sanitize: { // By using the address sanitizer, we not only uncover any issues // with the test, but also any issues with the code under test. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h index 3faa068c03..7c10fa57ec 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h @@ -18,9 +18,10 @@ #include <TimeStats/TimeStats.h> #include <utils/Timers.h> - #include <memory> +#include "Feature.h" + namespace android { class HWComposer; @@ -55,9 +56,9 @@ public: virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0; virtual renderengine::RenderEngine& getRenderEngine() const = 0; - virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0; + virtual void setRenderEngine(renderengine::RenderEngine*) = 0; - virtual TimeStats& getTimeStats() const = 0; + virtual TimeStats* getTimeStats() const = 0; virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0; virtual bool needsAnotherUpdate() const = 0; @@ -72,6 +73,8 @@ public: // TODO(b/121291683): These will become private/internal virtual void preComposition(CompositionRefreshArgs&) = 0; + virtual FeatureFlags getFeatureFlags() const = 0; + // Debugging virtual void dump(std::string&) const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index f201751d59..415a04113c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -32,6 +32,11 @@ namespace android::compositionengine { using Layers = std::vector<sp<compositionengine::LayerFE>>; using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>; +struct BorderRenderInfo { + float width = 0; + half4 color; + std::vector<int32_t> layerIds; +}; /** * A parameter object for refreshing a set of outputs */ @@ -47,6 +52,9 @@ struct CompositionRefreshArgs { // All the layers that have queued updates. Layers layersWithQueuedFrames; + // All graphic buffers that will no longer be used and should be removed from caches. + std::vector<uint64_t> bufferIdsToUncache; + // Controls how the color mode is chosen for an output OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced}; @@ -90,6 +98,8 @@ struct CompositionRefreshArgs { // If set, a frame has been scheduled for that time. std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime; + + std::vector<BorderRenderInfo> borderInfoList; }; } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h index c80fde6ead..ee8000ae18 100644 --- a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -13,19 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #pragma once -#include <future> +#include <ftl/flags.h> +#include <cstdint> namespace android::compositionengine { -namespace { -template <class T> -std::future<T> futureOf(T obj) { - std::promise<T> resultPromise; - std::future<T> resultFuture = resultPromise.get_future(); - resultPromise.set_value(std::move(obj)); - return resultFuture; -} -} // namespace -} // namespace android::compositionengine +enum class Feature : int32_t { + kSnapshotLayerMetadata = 1 << 0, +}; + +using FeatureFlags = ftl::Flags<Feature>; + +} // namespace android::compositionengine
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index ec610c1b1d..608c53a21a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -20,8 +20,6 @@ #include <ostream> #include <unordered_set> -#include <compositionengine/FenceResult.h> - // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -33,6 +31,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" #include <ftl/future.h> +#include <ui/FenceResult.h> #include <utils/RefBase.h> #include <utils/Timers.h> @@ -40,6 +39,10 @@ namespace android { class Fence; +namespace gui { +struct LayerMetadata; +} + namespace compositionengine { struct LayerFECompositionState; @@ -54,31 +57,8 @@ public: // Called before composition starts. Should return true if this layer has // pending updates which would require an extra display refresh cycle to // process. - virtual bool onPreComposition(nsecs_t refreshStartTime) = 0; - - // Used with latchCompositionState() - enum class StateSubset { - // Gets the basic geometry (bounds, transparent region, visibility, - // transforms, alpha) for the layer, for computing visibility and - // coverage. - BasicGeometry, - - // Gets the full geometry (crops, buffer transforms, metadata) and - // content (buffer or color) state for the layer. - GeometryAndContent, - - // Gets the per frame content (buffer or color) state for the layer. - Content, - - // Gets the cursor state for the layer. - Cursor, - }; - - // Prepares the output-independent composition state for the layer. The - // StateSubset argument selects what portion of the state is actually needed - // by the CompositionEngine code, since computing everything may be - // expensive. - virtual void prepareCompositionState(StateSubset) = 0; + virtual bool onPreComposition(nsecs_t refreshStartTime, + bool updatingOutputGeometryThisFrame) = 0; struct ClientCompositionTargetSettings { enum class BlurSetting { @@ -138,6 +118,9 @@ public: // Requested white point of the layer in nits const float whitePointNits; + + // True if layers with 170M dataspace should be overridden to sRGB. + const bool treat170mAsSrgb; }; // A superset of LayerSettings required by RenderEngine to compose a layer @@ -150,11 +133,11 @@ public: uint64_t frameNumber = 0; }; - // Returns the z-ordered list of LayerSettings to pass to RenderEngine::drawLayers. The list - // may contain shadows casted by the layer or the content of the layer itself. If the layer - // does not render then an empty list will be returned. - virtual std::vector<LayerSettings> prepareClientCompositionList( - ClientCompositionTargetSettings&) = 0; + // Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows + // casted by the layer or the content of the layer itself. If the layer does not render then an + // empty optional will be returned. + virtual std::optional<LayerSettings> prepareClientComposition( + ClientCompositionTargetSettings&) const = 0; // Called after the layer is displayed to update the presentation fence virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>) = 0; @@ -168,6 +151,8 @@ public: // Whether the layer should be rendered with rounded corners. virtual bool hasRoundedCorners() const = 0; virtual void setWasClientComposed(const sp<Fence>&) {} + virtual const gui::LayerMetadata* getMetadata() const = 0; + virtual const gui::LayerMetadata* getRelativeMetadata() const = 0; }; // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 974f7c6134..ad98e93232 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -163,7 +163,6 @@ struct LayerFECompositionState { // The buffer and related state sp<GraphicBuffer> buffer; - int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT}; sp<Fence> acquireFence = Fence::NO_FENCE; Region surfaceDamage; uint64_t frameNumber = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 2203639b1a..bd43c897a0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -23,6 +23,7 @@ #include <type_traits> #include <unordered_map> #include <utility> +#include <vector> #include <compositionengine/LayerFE.h> #include <renderengine/LayerSettings.h> @@ -262,9 +263,6 @@ public: // Presents the output, finalizing all composition details virtual void present(const CompositionRefreshArgs&) = 0; - // Latches the front-end layer state for each output layer - virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0; - // Enables predicting composition strategy to run client composition earlier virtual void setPredictCompositionStrategy(bool) = 0; @@ -275,6 +273,7 @@ protected: virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0; virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0; + virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0; virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0; virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0; virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index bf5184e997..4dbf8d2fce 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -19,6 +19,7 @@ #include <cstdint> #include <optional> #include <string> +#include <vector> #include <ui/Transform.h> #include <utils/StrongPointer.h> @@ -81,6 +82,10 @@ public: // TODO(lpique): Make this protected once it is only internally called. virtual CompositionState& editState() = 0; + // Clear the cache entries for a set of buffers that SurfaceFlinger no + // longer cares about. + virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0; + // Recalculates the state of the output layer from the output-independent // layer. If includeGeometry is false, the geometry state can be skipped. // internalDisplayRotationFlags must be set to the rotation flags for the @@ -126,9 +131,9 @@ public: // Returns true if the composition settings scale pixels virtual bool needsFiltering() const = 0; - // Returns a composition list to be used by RenderEngine if the layer has been overridden + // Returns LayerSettings to be used by RenderEngine if the layer has been overridden // during the composition process - virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0; + virtual std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() 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 9ee779cca1..585467456c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h @@ -91,16 +91,9 @@ public: // Called after the HWC calls are made to present the display virtual void onPresentDisplayCompleted() = 0; - // Called after the surface has been rendering to signal the surface should - // be made ready for displaying - virtual void flip() = 0; - // Debugging - Dumps the state of the RenderSurface to a string virtual void dump(std::string& result) const = 0; - // Debugging - gets the page flip count for the RenderSurface - virtual std::uint32_t getPageFlipCount() const = 0; - // Returns true if the render surface supports client composition prediction. virtual bool supportsCompositionStrategyPrediction() const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h index 386808d714..c6995576a1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h @@ -34,9 +34,9 @@ public: void setHwComposer(std::unique_ptr<HWComposer>) override; renderengine::RenderEngine& getRenderEngine() const override; - void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override; + void setRenderEngine(renderengine::RenderEngine*) override; - TimeStats& getTimeStats() const override; + TimeStats* getTimeStats() const override; void setTimeStats(const std::shared_ptr<TimeStats>&) override; bool needsAnotherUpdate() const override; @@ -48,17 +48,17 @@ public: void preComposition(CompositionRefreshArgs&) override; + FeatureFlags getFeatureFlags() const override; + // Debugging void dump(std::string&) const override; - void updateLayerStateFromFE(CompositionRefreshArgs& args); - // Testing void setNeedsAnotherUpdateForTest(bool); private: std::unique_ptr<HWComposer> mHwComposer; - std::unique_ptr<renderengine::RenderEngine> mRenderEngine; + renderengine::RenderEngine* mRenderEngine; std::shared_ptr<TimeStats> mTimeStats; bool mNeedsAnotherUpdate = false; nsecs_t mRefreshStartTime = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h index fd22aa3a2a..6e9ea6ffd8 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h @@ -17,7 +17,8 @@ #pragma once #include <cstdint> -#include <vector> +#include <stack> +#include <unordered_map> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -37,35 +38,76 @@ class GraphicBuffer; namespace compositionengine::impl { -// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each -// HWC display and layer. When updating a display target or a layer buffer, -// we have the option to send the buffer handle over or to request the HAL to -// retrieve it from its cache. The latter is cheaper since it eliminates the -// overhead to transfer the handle over the trasport layer, and the overhead -// for the HAL to clone and retain the handle. +// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases +// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as +// an optimization. +struct HwcSlotAndBuffer { + uint32_t slot; + sp<GraphicBuffer> buffer; +}; + +// +// Manages the slot assignments for a buffers stored in Composer HAL's cache. +// +// Cache slots are an optimization when communicating buffer handles to Composer +// HAL. When updating a layer's buffer, we can either send a new buffer handle +// along with it's slot assignment or request the HAL to reuse a buffer handle +// that we've already sent by using the slot assignment. The latter is cheaper +// since it eliminates the overhead to transfer the buffer handle over IPC and +// the overhead for the HAL to clone the handle. // -// To be able to find out whether a buffer is already in the HAL's cache, we -// use HWComposerBufferCache to mirror the cache in SF. class HwcBufferCache { +private: + static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS; + public: + // public for testing + // Override buffers don't use the normal cache slots because we don't want them to evict client + // buffers from the cache. We add an extra slot at the end for the override buffers. + static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount; + HwcBufferCache(); - // Given a buffer, return the HWC cache slot and - // buffer to be sent to HWC. + + // + // Given a buffer, return the HWC cache slot and buffer to send to HWC. + // + // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the + // buffer handle. // - // 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, - sp<GraphicBuffer>* outBuffer); + HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer); + // + // Given a buffer, return the HWC cache slot and buffer to send to HWC. + // + // A special slot number is used for override buffers. + // + // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the + // buffer handle. + // + HwcSlotAndBuffer getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer); - // Special caching slot for the layer caching feature. - static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS; + // + // When a client process discards a buffer, it needs to be purged from the HWC cache. + // + // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache. + // + uint32_t uncache(uint64_t graphicBufferId); private: - // 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. - static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; - wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount]; + uint32_t cache(const sp<GraphicBuffer>& buffer); + uint32_t getLeastRecentlyUsedSlot(); + + struct Cache { + sp<GraphicBuffer> buffer; + uint32_t slot; + // Cache entries are evicted according to least-recently-used when more than + // kMaxLayerBufferCount unique buffers have been sent to a layer. + uint64_t lruCounter; + }; + + std::unordered_map<uint64_t, Cache> mCacheByBufferId; + sp<GraphicBuffer> mLastOverrideBuffer; + std::stack<uint32_t> mFreeSlots; + uint64_t mLeastRecentlyUsedCounter; }; } // namespace compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 428c19fe02..1393e29cc2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -82,6 +82,7 @@ public: void prepare(const CompositionRefreshArgs&, LayerFESet&) override; void present(const CompositionRefreshArgs&) override; + void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override; void collectVisibleLayers(const CompositionRefreshArgs&, compositionengine::Output::CoverageState&) override; @@ -89,7 +90,6 @@ public: compositionengine::Output::CoverageState&) override; void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override; - void updateLayerStateFromFE(const CompositionRefreshArgs&) const override; void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override; void planComposition() override; void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override; @@ -135,9 +135,11 @@ protected: void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{}; bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; + virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const; std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*> &outLayerFEs) override; + virtual bool layerNeedsFiltering(const OutputLayer*) const; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; @@ -154,8 +156,11 @@ protected: bool mustRecompose() const; + const std::string& getNamePlusId() const { return mNamePlusId; } + private: void dirtyEntireOutput(); + void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&); compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const; void finishPrepareFrame(); ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; @@ -163,6 +168,7 @@ private: const compositionengine::CompositionRefreshArgs&) const; std::string mName; + std::string mNamePlusId; std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile; std::unique_ptr<compositionengine::RenderSurface> mRenderSurface; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index 7709b967b6..c29165210d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -33,6 +33,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" #include <compositionengine/ProjectionSpace.h> +#include <renderengine/BorderRenderInfo.h> #include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -164,6 +165,8 @@ struct OutputCompositionState { bool treat170mAsSrgb = false; + std::vector<renderengine::BorderRenderInfo> borderInfoList; + uint64_t lastOutputLayerHash = 0; uint64_t outputLayerHash = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index ecd432f629..f383392e55 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -18,7 +18,9 @@ #include <cstdint> #include <memory> +#include <optional> #include <string> +#include <vector> #include <compositionengine/LayerFE.h> #include <compositionengine/OutputLayer.h> @@ -43,6 +45,8 @@ public: void setHwcLayer(std::shared_ptr<HWC2::Layer>) override; + void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; + void updateCompositionState(bool includeGeometry, bool forceClientComposition, ui::Transform::RotationFlags) override; void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden, @@ -57,7 +61,7 @@ public: void prepareForDeviceLayerRequests() override; void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override; bool needsFiltering() const override; - std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override; + std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override; void dump(std::string&) const override; virtual FloatRect calculateOutputSourceCrop(uint32_t internalDisplayRotationFlags) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h index e4cb113645..1c14a43920 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h @@ -62,15 +62,12 @@ public: base::unique_fd* bufferFence) override; void queueBuffer(base::unique_fd readyFence) override; void onPresentDisplayCompleted() override; - void flip() override; bool supportsCompositionStrategyPrediction() const override; // Debugging void dump(std::string& result) const override; - std::uint32_t getPageFlipCount() const override; // Testing - void setPageFlipCountForTest(std::uint32_t); void setSizeForTest(const ui::Size&); std::shared_ptr<renderengine::ExternalTexture>& mutableTextureForTest(); base::unique_fd& mutableBufferReadyForTest(); @@ -89,7 +86,6 @@ private: ui::Size mSize; const size_t mMaxTextureCacheSize; bool mProtected{false}; - std::uint32_t mPageFlipCount{0}; }; std::unique_ptr<compositionengine::RenderSurface> createRenderSurface( diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index 5aec7c2e88..e309442220 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -72,6 +72,7 @@ enum class LayerStateField : uint32_t { SolidColor = 1u << 16, BackgroundBlurRadius = 1u << 17, BlurRegions = 1u << 18, + HasProtectedContent = 1u << 19, }; // clang-format on @@ -245,9 +246,9 @@ public: ui::Dataspace getDataspace() const { return mOutputDataspace.get(); } - bool isProtected() const { - return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent; - } + wp<GraphicBuffer> getBuffer() const { return mBuffer.get(); } + + bool isProtected() const { return mIsProtected.get(); } bool hasSolidColorCompositionType() const { return getOutputLayer()->getLayerFE().getCompositionState()->compositionType == @@ -482,7 +483,11 @@ private: return hash; }}; - static const constexpr size_t kNumNonUniqueFields = 17; + OutputLayerState<bool, LayerStateField::HasProtectedContent> mIsProtected{[](auto layer) { + return layer->getLayerFE().getCompositionState()->hasProtectedContent; + }}; + + static const constexpr size_t kNumNonUniqueFields = 18; std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() { std::array<const StateInterface*, kNumNonUniqueFields> constFields = @@ -501,7 +506,7 @@ private: &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace, &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions, - &mFrameNumber, + &mFrameNumber, &mIsProtected, }; } }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h index f953d0b7d0..9b2387b966 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h @@ -40,9 +40,9 @@ public: MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>)); MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&()); - MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>)); + MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*)); - MOCK_CONST_METHOD0(getTimeStats, TimeStats&()); + MOCK_CONST_METHOD0(getTimeStats, TimeStats*()); MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&)); MOCK_CONST_METHOD0(needsAnotherUpdate, bool()); @@ -53,6 +53,8 @@ public: MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&)); + MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags()); + MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 1c5c10f823..14922a4e0d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -42,18 +42,19 @@ public: MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*()); - MOCK_METHOD1(onPreComposition, bool(nsecs_t)); + MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool)); - MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset)); - MOCK_METHOD1(prepareClientCompositionList, - std::vector<compositionengine::LayerFE::LayerSettings>( - compositionengine::LayerFE::ClientCompositionTargetSettings&)); + MOCK_CONST_METHOD1(prepareClientComposition, + std::optional<compositionengine::LayerFE::LayerSettings>( + compositionengine::LayerFE::ClientCompositionTargetSettings&)); MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>), (override)); MOCK_CONST_METHOD0(getDebugName, const char*()); MOCK_CONST_METHOD0(getSequence, int32_t()); MOCK_CONST_METHOD0(hasRoundedCorners, bool()); + MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*()); + MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*()); }; } // 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 2a04949cff..18e6879bdb 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -82,6 +82,7 @@ public: MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&)); MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&)); + MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&)); MOCK_METHOD2(rebuildLayerStacks, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&)); MOCK_METHOD2(collectVisibleLayers, @@ -91,7 +92,6 @@ public: void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&)); MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&)); - MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&)); MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&)); MOCK_METHOD0(planComposition, void()); MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&)); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index a6cb811468..5fef63a510 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -16,6 +16,8 @@ #pragma once +#include <optional> + #include <compositionengine/CompositionEngine.h> #include <compositionengine/LayerFE.h> #include <compositionengine/Output.h> @@ -33,6 +35,8 @@ public: MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>)); + MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&)); + MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&()); MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&()); @@ -51,7 +55,7 @@ public: MOCK_METHOD0(prepareForDeviceLayerRequests, void()); MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request)); MOCK_CONST_METHOD0(needsFiltering, bool()); - MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>()); + MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>()); 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 e12aebb50c..af8d4bcb0b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h @@ -42,9 +42,7 @@ public: MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*)); MOCK_METHOD1(queueBuffer, void(base::unique_fd)); MOCK_METHOD0(onPresentDisplayCompleted, void()); - MOCK_METHOD0(flip, void()); MOCK_CONST_METHOD1(dump, void(std::string& result)); - MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t()); MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool()); }; diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index 6203dc6737..15fadbc8ee 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -65,15 +65,15 @@ void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) { } renderengine::RenderEngine& CompositionEngine::getRenderEngine() const { - return *mRenderEngine.get(); + return *mRenderEngine; } -void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mRenderEngine = std::move(renderEngine); +void CompositionEngine::setRenderEngine(renderengine::RenderEngine* renderEngine) { + mRenderEngine = renderEngine; } -TimeStats& CompositionEngine::getTimeStats() const { - return *mTimeStats.get(); +TimeStats* CompositionEngine::getTimeStats() const { + return mTimeStats.get(); } void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) { @@ -105,8 +105,6 @@ void CompositionEngine::present(CompositionRefreshArgs& args) { } } - updateLayerStateFromFE(args); - for (const auto& output : args.outputs) { output->present(args); } @@ -119,8 +117,6 @@ void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) { for (const auto& output : args.outputs) { for (auto* layer : output->getOutputLayersOrderedByZ()) { if (layer->isHardwareCursor()) { - // Latch the cursor composition state from each front-end layer. - layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor); layer->writeCursorPositionToHWC(); } } @@ -136,7 +132,7 @@ void CompositionEngine::preComposition(CompositionRefreshArgs& args) { mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); for (auto& layer : args.layers) { - if (layer->onPreComposition(mRefreshStartTime)) { + if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) { needsAnotherUpdate = true; } } @@ -144,6 +140,10 @@ void CompositionEngine::preComposition(CompositionRefreshArgs& args) { mNeedsAnotherUpdate = needsAnotherUpdate; } +FeatureFlags CompositionEngine::getFeatureFlags() const { + return {}; +} + void CompositionEngine::dump(std::string&) const { // The base class has no state to dump, but derived classes might. } @@ -152,12 +152,5 @@ void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) { mNeedsAnotherUpdate = value; } -void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) { - // Update the composition state from each front-end layer - for (const auto& output : args.outputs) { - output->updateLayerStateFromFE(args); - } -} - } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 163d9a3748..24669c2ff5 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -25,6 +25,7 @@ #include <compositionengine/impl/DumpHelpers.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/RenderSurface.h> +#include <gui/TraceUtils.h> #include <utils/Trace.h> @@ -196,7 +197,7 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& }); if (hasQueuedFrames) { - releasedLayers.emplace_back(layerFE); + releasedLayers.emplace_back(wp<LayerFE>::fromExisting(layerFE)); } } @@ -235,7 +236,7 @@ void Display::beginFrame() { bool Display::chooseCompositionStrategy( std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { - ATRACE_CALL(); + ATRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str()); ALOGV(__FUNCTION__); if (mIsDisconnected) { @@ -248,12 +249,17 @@ bool Display::chooseCompositionStrategy( return false; } - const nsecs_t startTime = systemTime(); - // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); const bool requiresClientComposition = anyLayersRequireClientComposition(); + + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + } + + const TimePoint hwcValidateStartTime = TimePoint::now(); + if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, getState().earliestPresentTime, @@ -266,8 +272,10 @@ bool Display::chooseCompositionStrategy( } if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime()); - mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now()); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + } } return true; @@ -370,7 +378,7 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { auto& hwc = getCompositionEngine().getHwComposer(); - const nsecs_t startTime = systemTime(); + const TimePoint startTime = TimePoint::now(); if (isPowerHintSessionEnabled()) { if (!getCompositionEngine().getHwComposer().getComposer()->isSupported( @@ -384,7 +392,7 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { getState().previousPresentFence); if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime()); + mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now()); } fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); @@ -432,13 +440,6 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre } impl::Output::finishFrame(refreshArgs, std::move(result)); - - if (isPowerHintSessionEnabled()) { - auto& hwc = getCompositionEngine().getHwComposer(); - if (auto halDisplayId = HalDisplayId::tryCast(mId)) { - mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); - } - } } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp index f95382d921..d64fd5707c 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp @@ -16,43 +16,80 @@ #include <compositionengine/impl/HwcBufferCache.h> -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" - namespace android::compositionengine::impl { HwcBufferCache::HwcBufferCache() { - std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr)); + for (uint32_t i = 0; i < kMaxLayerBufferCount; i++) { + mFreeSlots.push(i); + } } -void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, - sp<GraphicBuffer>* outBuffer) { - // default is 0 - if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 || - slot >= static_cast<int32_t>(kMaxLayerBufferCount)) { - *outSlot = 0; - } else { - *outSlot = static_cast<uint32_t>(slot); +HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) { + // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send + // in nullptrs. + if (buffer == nullptr) { + return {0, nullptr}; + } + if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) { + Cache& cache = i->second; + // mark this cache slot as more recently used so it won't get evicted anytime soon + cache.lruCounter = mLeastRecentlyUsedCounter++; + return {cache.slot, nullptr}; } + return {cache(buffer), buffer}; +} - auto& currentBuffer = mBuffers[*outSlot]; - wp<GraphicBuffer> weakCopy(buffer); - if (currentBuffer == weakCopy) { - // already cached in HWC, skip sending the buffer - *outBuffer = nullptr; - } else { - *outBuffer = buffer; +HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer) { + if (buffer == mLastOverrideBuffer) { + return {kOverrideBufferSlot, nullptr}; + } + mLastOverrideBuffer = buffer; + return {kOverrideBufferSlot, buffer}; +} + +uint32_t HwcBufferCache::uncache(uint64_t bufferId) { + if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) { + uint32_t slot = i->second.slot; + mCacheByBufferId.erase(i); + mFreeSlots.push(slot); + return slot; + } + if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) { + mLastOverrideBuffer = nullptr; + return kOverrideBufferSlot; + } + return UINT32_MAX; +} + +uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) { + Cache cache; + cache.slot = getLeastRecentlyUsedSlot(); + cache.lruCounter = mLeastRecentlyUsedCounter++; + cache.buffer = buffer; + mCacheByBufferId.emplace(buffer->getId(), cache); + return cache.slot; +} - // update cache - currentBuffer = buffer; +uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() { + if (mFreeSlots.empty()) { + assert(!mCacheByBufferId.empty()); + // evict the least recently used cache entry + auto cacheToErase = mCacheByBufferId.begin(); + for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) { + if (i->second.lruCounter < cacheToErase->second.lruCounter) { + cacheToErase = i; + } + } + uint32_t slot = cacheToErase->second.slot; + mCacheByBufferId.erase(cacheToErase); + mFreeSlots.push(slot); } + uint32_t slot = mFreeSlots.top(); + mFreeSlots.pop(); + return slot; } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 6631a2772c..a405c4df88 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -106,7 +106,6 @@ void LayerFECompositionState::dump(std::string& out) const { dumpVal(out, "composition type", toString(compositionType), compositionType); out.append("\n buffer: "); - dumpVal(out, "slot", bufferSlot); dumpVal(out, "buffer", buffer.get()); out.append("\n "); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index d0c5803d42..16ef812d04 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -29,6 +29,7 @@ #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/impl/planner/Planner.h> #include <ftl/future.h> +#include <gui/TraceUtils.h> #include <thread> @@ -116,6 +117,9 @@ const std::string& Output::getName() const { void Output::setName(const std::string& name) { mName = name; + auto displayIdOpt = getDisplayId(); + mNamePlusId = base::StringPrintf("%s (%s)", mName.c_str(), + displayIdOpt ? to_string(*displayIdOpt).c_str() : "NA"); } void Output::setCompositionEnabled(bool enabled) { @@ -424,10 +428,11 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ALOGV(__FUNCTION__); rebuildLayerStacks(refreshArgs, geomSnapshots); + uncacheBuffers(refreshArgs.bufferIdsToUncache); } void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) { - ATRACE_CALL(); + ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); ALOGV(__FUNCTION__); updateColorProfile(refreshArgs); @@ -451,6 +456,15 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg renderCachedSets(refreshArgs); } +void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { + if (bufferIdsToUncache.empty()) { + return; + } + for (auto outputLayer : getOutputLayersOrderedByZ()) { + outputLayer->uncacheBuffers(bufferIdsToUncache); + } +} + void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs, LayerFESet& layerFESet) { ATRACE_CALL(); @@ -501,7 +515,6 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // appear on multiple outputs. if (!coverage.latchedLayers.count(layerFE)) { coverage.latchedLayers.insert(layerFE); - layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry); } // Only consider the layers on this output @@ -725,14 +738,6 @@ void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) // The base class does nothing with this call. } -void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const { - for (auto* layer : getOutputLayersOrderedByZ()) { - layer->getLayerFE().prepareCompositionState( - args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent - : LayerFE::StateSubset::Content); - } -} - void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) { ATRACE_CALL(); ALOGV(__FUNCTION__); @@ -754,6 +759,44 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA forceClientComposition = false; } } + + updateCompositionStateForBorder(refreshArgs); +} + +void Output::updateCompositionStateForBorder( + const compositionengine::CompositionRefreshArgs& refreshArgs) { + std::unordered_map<int32_t, const Region*> layerVisibleRegionMap; + // Store a map of layerId to their computed visible region. + for (auto* layer : getOutputLayersOrderedByZ()) { + int layerId = (layer->getLayerFE()).getSequence(); + layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion); + } + OutputCompositionState& outputCompositionState = editState(); + outputCompositionState.borderInfoList.clear(); + bool clientComposeTopLayer = false; + for (const auto& borderInfo : refreshArgs.borderInfoList) { + renderengine::BorderRenderInfo info; + for (const auto& id : borderInfo.layerIds) { + info.combinedRegion.orSelf(*(layerVisibleRegionMap[id])); + } + + if (!info.combinedRegion.isEmpty()) { + info.width = borderInfo.width; + info.color = borderInfo.color; + outputCompositionState.borderInfoList.emplace_back(std::move(info)); + clientComposeTopLayer = true; + } + } + + // In this situation we must client compose the top layer instead of using hwc + // because we want to draw the border above all else. + // This could potentially cause a bit of a performance regression if the top + // layer would have been rendered using hwc originally. + // TODO(b/227656283): Measure system's performance before enabling the border feature + if (clientComposeTopLayer) { + auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1); + (topLayer->editState()).forceClientComposition = true; + } } void Output::planComposition() { @@ -871,6 +914,13 @@ ui::Dataspace Output::getBestDataspace(ui::Dataspace* outHdrDataSpace, ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB; *outHdrDataSpace = ui::Dataspace::UNKNOWN; + // An Output's layers may be stale when it is disabled. As a consequence, the layers returned by + // getOutputLayersOrderedByZ may not be in a valid state and it is not safe to access their + // properties. Return a default dataspace value in this case. + if (!getState().isEnabled) { + return ui::Dataspace::V0_SRGB; + } + for (const auto* layer : getOutputLayersOrderedByZ()) { switch (layer->getLayerFE().getCompositionState()->dataspace) { case ui::Dataspace::V0_SCRGB: @@ -1122,7 +1172,8 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi if (isPowerHintSessionEnabled()) { // get fence end time to know when gpu is complete in display - setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get())))); + setHintSessionGpuFence( + std::make_unique<FenceTime>(sp<Fence>::make(dup(optReadyFence->get())))); } // swap buffers (presentation) mRenderSurface->queueBuffer(std::move(*optReadyFence)); @@ -1141,15 +1192,9 @@ void Output::updateProtectedContentState() { bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) { return layer->getLayerFE().getCompositionState()->hasProtectedContent; }); - if (needsProtected != renderEngine.isProtected()) { - renderEngine.useProtectedContext(needsProtected); - } - if (needsProtected != mRenderSurface->isProtected() && - needsProtected == renderEngine.isProtected()) { + if (needsProtected != mRenderSurface->isProtected()) { mRenderSurface->setProtected(needsProtected); } - } else if (!outputState.isSecure && renderEngine.isProtected()) { - renderEngine.useProtectedContext(false); } } @@ -1195,33 +1240,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( ALOGV("hasClientComposition"); - renderengine::DisplaySettings clientCompositionDisplay; - clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent(); - clientCompositionDisplay.clip = outputState.layerStackSpace.getContent(); - clientCompositionDisplay.orientation = - ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); - clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() - ? outputState.dataspace - : ui::Dataspace::UNKNOWN; - - // If we have a valid current display brightness use that, otherwise fall back to the - // display's max desired - clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f - ? outputState.displayBrightnessNits - : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); - clientCompositionDisplay.maxLuminance = - mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); - clientCompositionDisplay.targetLuminanceNits = - outputState.clientTargetBrightness * outputState.displayBrightnessNits; - clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage; - clientCompositionDisplay.renderIntent = - static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>( - outputState.renderIntent); - - // Compute the global color transform matrix. - clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; - clientCompositionDisplay.deviceHandlesColorTransform = - outputState.usesDeviceComposition || getSkipColorTransform(); + renderengine::DisplaySettings clientCompositionDisplay = + generateClientCompositionDisplaySettings(); // Generate the client composition requests for the layers on this output. auto& renderEngine = getCompositionEngine().getRenderEngine(); @@ -1284,11 +1304,10 @@ std::optional<base::unique_fd> Output::composeSurfaces( // over to RenderEngine, in which case this flag can be removed from the drawLayers interface. const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay; - auto fenceResult = - toFenceResult(renderEngine - .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, - tex, useFramebufferCache, std::move(fd)) - .get()); + auto fenceResult = renderEngine + .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex, + useFramebufferCache, std::move(fd)) + .get(); if (mClientCompositionRequestCache && fenceStatus(fenceResult) != NO_ERROR) { // If rendering was not successful, remove the request from the cache. @@ -1297,10 +1316,13 @@ std::optional<base::unique_fd> Output::composeSurfaces( const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE); - if (auto& timeStats = getCompositionEngine().getTimeStats(); fence->isValid()) { - timeStats.recordRenderEngineDuration(renderEngineStart, std::make_shared<FenceTime>(fence)); - } else { - timeStats.recordRenderEngineDuration(renderEngineStart, systemTime()); + if (auto timeStats = getCompositionEngine().getTimeStats()) { + if (fence->isValid()) { + timeStats->recordRenderEngineDuration(renderEngineStart, + std::make_shared<FenceTime>(fence)); + } else { + timeStats->recordRenderEngineDuration(renderEngineStart, systemTime()); + } } for (auto* clientComposedLayer : clientCompositionLayersFE) { @@ -1310,6 +1332,47 @@ std::optional<base::unique_fd> Output::composeSurfaces( return base::unique_fd(fence->dup()); } +renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings() const { + const auto& outputState = getState(); + + renderengine::DisplaySettings clientCompositionDisplay; + clientCompositionDisplay.namePlusId = mNamePlusId; + clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent(); + clientCompositionDisplay.clip = outputState.layerStackSpace.getContent(); + clientCompositionDisplay.orientation = + ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); + clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() + ? outputState.dataspace + : ui::Dataspace::UNKNOWN; + + // If we have a valid current display brightness use that, otherwise fall back to the + // display's max desired + clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f + ? outputState.displayBrightnessNits + : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.maxLuminance = + mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.targetLuminanceNits = + outputState.clientTargetBrightness * outputState.displayBrightnessNits; + clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage; + clientCompositionDisplay.renderIntent = + static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>( + outputState.renderIntent); + + // Compute the global color transform matrix. + clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; + for (auto& info : outputState.borderInfoList) { + renderengine::BorderRenderInfo borderInfo; + borderInfo.width = info.width; + borderInfo.color = info.color; + borderInfo.combinedRegion = info.combinedRegion; + clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo)); + } + clientCompositionDisplay.deviceHandlesColorTransform = + outputState.usesDeviceComposition || getSkipColorTransform(); + return clientCompositionDisplay; +} + std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) { std::vector<LayerFE::LayerSettings> clientCompositionLayers; @@ -1320,7 +1383,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( bool firstLayer = true; bool disableBlurs = false; - sp<GraphicBuffer> previousOverrideBuffer = nullptr; + uint64_t previousOverrideBufferId = 0; for (auto* layer : getOutputLayersOrderedByZ()) { const auto& layerState = layer->getState(); @@ -1356,11 +1419,10 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty(); if (clientComposition || clearClientComposition) { - std::vector<LayerFE::LayerSettings> results; - if (layer->getState().overrideInfo.buffer != nullptr) { - if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) { - results = layer->getOverrideCompositionList(); - previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer(); + if (auto overrideSettings = layer->getOverrideCompositionSettings()) { + if (overrideSettings->bufferId != previousOverrideBufferId) { + previousOverrideBufferId = overrideSettings->bufferId; + clientCompositionLayers.push_back(std::move(*overrideSettings)); ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName()); } else { ALOGV("Skipping redundant override buffer for [%s] in RE", @@ -1376,7 +1438,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( Enabled); compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{.clip = clip, - .needsFiltering = layer->needsFiltering() || + .needsFiltering = layerNeedsFiltering(layer) || outputState.needsFiltering, .isSecure = outputState.isSecure, .supportsProtectedContent = supportsProtectedContent, @@ -1385,21 +1447,20 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( .realContentIsVisible = realContentIsVisible, .clearContent = !clientComposition, .blurSetting = blurSetting, - .whitePointNits = layerState.whitePointNits}; - results = layerFE.prepareClientCompositionList(targetSettings); - if (realContentIsVisible && !results.empty()) { - layer->editState().clientCompositionTimestamp = systemTime(); + .whitePointNits = layerState.whitePointNits, + .treat170mAsSrgb = outputState.treat170mAsSrgb}; + if (auto clientCompositionSettings = + layerFE.prepareClientComposition(targetSettings)) { + clientCompositionLayers.push_back(std::move(*clientCompositionSettings)); + if (realContentIsVisible) { + layer->editState().clientCompositionTimestamp = systemTime(); + } } } if (clientComposition) { outLayerFEs.push_back(&layerFE); } - - clientCompositionLayers.insert(clientCompositionLayers.end(), - std::make_move_iterator(results.begin()), - std::make_move_iterator(results.end())); - results.clear(); } firstLayer = false; @@ -1408,6 +1469,10 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( return clientCompositionLayers; } +bool Output::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const { + return layer->needsFiltering(); +} + void Output::appendRegionFlashRequests( const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) { if (flashRegion.isEmpty()) { @@ -1438,7 +1503,7 @@ bool Output::isPowerHintSessionEnabled() { } void Output::postFramebuffer() { - ATRACE_CALL(); + ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); ALOGV(__FUNCTION__); if (!getState().isEnabled) { @@ -1447,7 +1512,6 @@ void Output::postFramebuffer() { auto& outputState = editState(); outputState.dirtyRegion.clear(); - mRenderSurface->flip(); auto frame = presentAndGetFrameFences(); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 948c0c90bf..c512a1e97f 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -62,14 +62,18 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits); dumpVal(out, "clientTargetBrightness", clientTargetBrightness); dumpVal(out, "displayBrightness", displayBrightness); - out.append("\n "); dumpVal(out, "compositionStrategyPredictionState", ftl::enum_string(strategyPrediction)); + out.append("\n "); out.append("\n "); dumpVal(out, "treate170mAsSrgb", treat170mAsSrgb); - out += '\n'; + out.append("\n"); + for (const auto& borderRenderInfo : borderInfoList) { + dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion); + } + out.append("\n"); } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 1bb9d0eb63..a7c24b628d 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -610,6 +610,19 @@ void OutputLayer::writeSidebandStateToHWC(HWC2::Layer* hwcLayer, } } +void OutputLayer::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { + auto& state = editState(); + // Skip doing this if there is no HWC interface + if (!state.hwc) { + return; + } + + for (auto bufferId : bufferIdsToUncache) { + state.hwc->hwcBufferCache.uncache(bufferId); + // TODO(b/258196272): send uncache requests to Composer HAL + } +} + void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, bool skipLayer) { @@ -622,27 +635,24 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, to_string(error).c_str(), static_cast<int32_t>(error)); } - sp<GraphicBuffer> buffer = outputIndependentState.buffer; - sp<Fence> acquireFence = outputIndependentState.acquireFence; - int slot = outputIndependentState.bufferSlot; + HwcSlotAndBuffer hwcSlotAndBuffer; + sp<Fence> hwcFence; + // Override buffers use a special cache slot so that they don't evict client buffers. if (getState().overrideInfo.buffer != nullptr && !skipLayer) { - buffer = getState().overrideInfo.buffer->getBuffer(); - acquireFence = getState().overrideInfo.acquireFence; - slot = HwcBufferCache::FLATTENER_CACHING_SLOT; + hwcSlotAndBuffer = editState().hwc->hwcBufferCache.getHwcSlotAndBufferForOverride( + getState().overrideInfo.buffer->getBuffer()); + hwcFence = getState().overrideInfo.acquireFence; + } else { + hwcSlotAndBuffer = + editState().hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer); + hwcFence = outputIndependentState.acquireFence; } - ALOGV("Writing buffer %p", buffer.get()); - - uint32_t hwcSlot = 0; - sp<GraphicBuffer> hwcBuffer; - // We need access to the output-dependent state for the buffer cache there, - // though otherwise the buffer is not output-dependent. - editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer); - - if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); + if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence); error != hal::Error::NONE) { - ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle, - to_string(error).c_str(), static_cast<int32_t>(error)); + ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), + hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(), + static_cast<int32_t>(error)); } } @@ -787,7 +797,7 @@ bool OutputLayer::needsFiltering() const { sourceCrop.getWidth() != displayFrame.getWidth(); } -std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const { +std::optional<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionSettings() const { if (getState().overrideInfo.buffer == nullptr) { return {}; } @@ -816,7 +826,7 @@ std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() co settings.alpha = 1.0f; settings.whitePointNits = getOutput().getState().sdrWhitePointNits; - return {static_cast<LayerFE::LayerSettings>(settings)}; + return settings; } void OutputLayer::dump(std::string& out) const { diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index 5a3af7bfea..0fe55db05c 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -251,10 +251,6 @@ void RenderSurface::onPresentDisplayCompleted() { mDisplaySurface->onFrameCommitted(); } -void RenderSurface::flip() { - mPageFlipCount++; -} - void RenderSurface::dump(std::string& out) const { using android::base::StringAppendF; @@ -265,7 +261,6 @@ void RenderSurface::dump(std::string& out) const { dumpVal(out, "size", mSize); StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(), ANativeWindow_getFormat(mNativeWindow.get())); - dumpVal(out, "flips", mPageFlipCount); out.append("\n"); String8 surfaceDump; @@ -273,14 +268,6 @@ void RenderSurface::dump(std::string& out) const { out.append(surfaceDump); } -std::uint32_t RenderSurface::getPageFlipCount() const { - return mPageFlipCount; -} - -void RenderSurface::setPageFlipCountForTest(std::uint32_t count) { - mPageFlipCount = count; -} - void RenderSurface::setSizeForTest(const ui::Size& size) { mSize = size; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index d6f02ee42a..ed9a88d3c4 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -193,11 +193,11 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te std::vector<renderengine::LayerSettings> layerSettings; renderengine::LayerSettings highlight; for (const auto& layer : mLayers) { - const auto clientCompositionList = - layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList( - targetSettings); - layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(), - clientCompositionList.cend()); + if (auto clientCompositionSettings = + layer.getState()->getOutputLayer()->getLayerFE().prepareClientComposition( + targetSettings)) { + layerSettings.push_back(std::move(*clientCompositionSettings)); + } } renderengine::LayerSettings blurLayerSettings; @@ -205,43 +205,40 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te auto blurSettings = targetSettings; blurSettings.blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly; - auto clientCompositionList = - mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList( - blurSettings); - blurLayerSettings = clientCompositionList.back(); + + auto blurLayerSettings = + mBlurLayer->getOutputLayer()->getLayerFE().prepareClientComposition(blurSettings); // This mimics Layer::prepareClearClientComposition - blurLayerSettings.skipContentDraw = true; - blurLayerSettings.name = std::string("blur layer"); + blurLayerSettings->skipContentDraw = true; + blurLayerSettings->name = std::string("blur layer"); // Clear out the shadow settings - blurLayerSettings.shadow = {}; - layerSettings.push_back(blurLayerSettings); + blurLayerSettings->shadow = {}; + layerSettings.push_back(std::move(*blurLayerSettings)); } - renderengine::LayerSettings holePunchSettings; - renderengine::LayerSettings holePunchBackgroundSettings; if (mHolePunchLayer) { auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE(); - auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings); - // Assume that the final layer contains the buffer that we want to - // replace with a hole punch. - holePunchSettings = clientCompositionList.back(); + + auto holePunchSettings = layerFE.prepareClientComposition(targetSettings); // This mimics Layer::prepareClearClientComposition - holePunchSettings.source.buffer.buffer = nullptr; - holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f); - holePunchSettings.disableBlending = true; - holePunchSettings.alpha = 0.0f; - holePunchSettings.name = + holePunchSettings->source.buffer.buffer = nullptr; + holePunchSettings->source.solidColor = half3(0.0f, 0.0f, 0.0f); + holePunchSettings->disableBlending = true; + holePunchSettings->alpha = 0.0f; + holePunchSettings->name = android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName()); - layerSettings.push_back(holePunchSettings); // Add a solid background as the first layer in case there is no opaque // buffer behind the punch hole + renderengine::LayerSettings holePunchBackgroundSettings; holePunchBackgroundSettings.alpha = 1.0f; holePunchBackgroundSettings.name = std::string("holePunchBackground"); - holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries; + holePunchBackgroundSettings.geometry.boundaries = holePunchSettings->geometry.boundaries; holePunchBackgroundSettings.geometry.positionTransform = - holePunchSettings.geometry.positionTransform; - layerSettings.emplace(layerSettings.begin(), holePunchBackgroundSettings); + holePunchSettings->geometry.positionTransform; + layerSettings.emplace(layerSettings.begin(), std::move(holePunchBackgroundSettings)); + + layerSettings.push_back(std::move(*holePunchSettings)); } if (sDebugHighlighLayers) { @@ -276,11 +273,10 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te constexpr bool kUseFramebufferCache = false; - auto fenceResult = - toFenceResult(renderEngine - .drawLayers(displaySettings, layerSettings, texture->get(), - kUseFramebufferCache, std::move(bufferFence)) - .get()); + auto fenceResult = renderEngine + .drawLayers(displaySettings, layerSettings, texture->get(), + kUseFramebufferCache, std::move(bufferFence)) + .get(); if (fenceStatus(fenceResult) == NO_ERROR) { mDrawFence = std::move(fenceResult).value_or(Fence::NO_FENCE); @@ -415,8 +411,8 @@ void CachedSet::dump(std::string& result) const { if (mLayers.size() == 1) { base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str()); - if (auto* buffer = mLayers[0].getBuffer().get()) { - base::StringAppendF(&result, " Buffer %p", buffer); + if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) { + base::StringAppendF(&result, " Buffer %p", buffer.get()); base::StringAppendF(&result, " Format %s", decodePixelFormat(buffer->getPixelFormat()).c_str()); } @@ -426,8 +422,8 @@ void CachedSet::dump(std::string& result) const { result.append(" Cached set of:\n"); for (const Layer& layer : mLayers) { base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str()); - if (auto* buffer = layer.getBuffer().get()) { - base::StringAppendF(&result, " Buffer %p", buffer); + if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) { + base::StringAppendF(&result, " Buffer %p", buffer.get()); base::StringAppendF(&result, " Format[%s]", decodePixelFormat(buffer->getPixelFormat()).c_str()); } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index de9de0150d..60ed660c7a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -62,17 +62,16 @@ TEST_F(CompositionEngineTest, canSetHWComposer) { } TEST_F(CompositionEngineTest, canSetRenderEngine) { - renderengine::mock::RenderEngine* renderEngine = - new StrictMock<renderengine::mock::RenderEngine>(); - mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine)); + auto renderEngine = std::make_unique<StrictMock<renderengine::mock::RenderEngine>>(); + mEngine.setRenderEngine(renderEngine.get()); - EXPECT_EQ(renderEngine, &mEngine.getRenderEngine()); + EXPECT_EQ(renderEngine.get(), &mEngine.getRenderEngine()); } TEST_F(CompositionEngineTest, canSetTimeStats) { mEngine.setTimeStats(mTimeStats); - EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats()); + EXPECT_EQ(mTimeStats.get(), mEngine.getTimeStats()); } /* @@ -108,12 +107,6 @@ TEST_F(CompositionEnginePresentTest, worksAsExpected) { EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _)); EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _)); - // The next step in presenting is to make sure all outputs have the latest - // state from the front-end (SurfaceFlinger). - EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs))); - EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs))); - EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs))); - // The last step is to actually present each output. EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))); EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs))); @@ -175,21 +168,18 @@ TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorL { InSequence seq; EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor)); EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC()); } { InSequence seq; EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor)); EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC()); } { InSequence seq; EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor)); EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC()); } @@ -222,9 +212,12 @@ TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWi nsecs_t ts1 = 0; nsecs_t ts2 = 0; nsecs_t ts3 = 0; - EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false))); - EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false))); - EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false))); + EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)) + .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false))); + EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)) + .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false))); + EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)) + .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false))); mRefreshArgs.outputs = {mOutput1}; mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE}; @@ -238,9 +231,9 @@ TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWi } TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) { - EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false)); mEngine.setNeedsAnotherUpdateForTest(true); @@ -255,9 +248,9 @@ TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) { TEST_F(CompositionTestPreComposition, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) { - EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true)); - EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false)); mRefreshArgs.outputs = {mOutput1}; mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE}; diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 5369642a1b..95459c0499 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -194,7 +194,7 @@ struct DisplayTestCommon : public testing::Test { StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; - sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>(); + sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make(); }; struct PartialMockDisplayTestCommon : public DisplayTestCommon { @@ -524,7 +524,7 @@ TEST_F(DisplaySetReleasedLayersTest, doesNothingIfGpuDisplay) { auto args = getDisplayCreationArgsForGpuVirtualDisplay(); std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args); - sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> layerXLayerFE = sp<StrictMock<mock::LayerFE>>::make(); { Output::ReleasedLayers releasedLayers; @@ -542,7 +542,7 @@ TEST_F(DisplaySetReleasedLayersTest, doesNothingIfGpuDisplay) { } TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) { - sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> layerXLayerFE = sp<StrictMock<mock::LayerFE>>::make(); { Output::ReleasedLayers releasedLayers; @@ -558,7 +558,7 @@ TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) { } TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) { - sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> unknownLayer = sp<StrictMock<mock::LayerFE>>::make(); CompositionRefreshArgs refreshArgs; refreshArgs.layersWithQueuedFrames.push_back(mLayer1.layerFE); @@ -897,9 +897,9 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnGpuDisplay) { } TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) { - sp<Fence> presentFence = new Fence(); - sp<Fence> layer1Fence = new Fence(); - sp<Fence> layer2Fence = new Fence(); + sp<Fence> presentFence = sp<Fence>::make(); + sp<Fence> layer1Fence = sp<Fence>::make(); + sp<Fence> layer2Fence = sp<Fence>::make(); EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _, _)) .Times(1); @@ -1046,8 +1046,8 @@ struct DisplayFunctionalTest : public testing::Test { NiceMock<android::mock::HWComposer> mHwComposer; NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; NiceMock<mock::CompositionEngine> mCompositionEngine; - sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>(); - sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>(); + sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make(); + sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make(); std::shared_ptr<Display> mDisplay; impl::RenderSurface* mRenderSurface; diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp index 00eafb15f5..cf72e8f8de 100644 --- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp @@ -22,64 +22,172 @@ namespace android::compositionengine { namespace { -class TestableHwcBufferCache : public impl::HwcBufferCache { -public: - void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, - sp<GraphicBuffer>* outBuffer) { - HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer); - } -}; +using impl::HwcBufferCache; +using impl::HwcSlotAndBuffer; 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()); + sp<GraphicBuffer> mBuffer1 = + sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); + sp<GraphicBuffer> mBuffer2 = + sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); +}; + +TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); + EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX); + EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1); + + HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2); + EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot); + EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX); + EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2); +} + +TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1); + EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX); + EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1); + + HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1); + EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot); + EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr); +} + +TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + sp<GraphicBuffer> graphicBuffers[100]; + HwcSlotAndBuffer slotsAndBuffers[100]; + int finalCachedBufferIndex = 0; + for (int i = 0; i < 100; ++i) { + graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); + slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]); + // we fill up the cache when the slot number for the first buffer is reused + if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) { + finalCachedBufferIndex = i; + break; + } } + ASSERT_GT(finalCachedBufferIndex, 1); + // the final cached buffer has the same slot value as the oldest buffer + EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot); + // the oldest buffer is no longer in the cache because it was evicted + EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX); +} - impl::HwcBufferCache 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, uncache_whenCached_returnsSlotNumber) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); + ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX); -TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) { - testSlot(0, 0); + HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2); + ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX); + + // the 1st buffer should be found in the cache with a slot number + EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot); + // since the 1st buffer has been previously uncached, we should no longer receive a slot number + EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX); + // the 2nd buffer should be still found in the cache with a slot number + EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot); + // since the 2nd buffer has been previously uncached, we should no longer receive a slot number + EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); } -TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) { - testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1); +TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); + ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX); + + EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); +} + +TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenCached_returnsSameSlotAndNullBuffer) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1); + EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX); + EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1); + + HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1); + EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot); + EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr); +} + +TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenSlotsFull_returnsIndependentSlot) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + sp<GraphicBuffer> graphicBuffers[100]; + HwcSlotAndBuffer slotsAndBuffers[100]; + int finalCachedBufferIndex = -1; + for (int i = 0; i < 100; ++i) { + graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); + slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]); + // we fill up the cache when the slot number for the first buffer is reused + if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) { + finalCachedBufferIndex = i; + break; + } + } + // expect to have cached at least a few buffers before evicting + ASSERT_GT(finalCachedBufferIndex, 1); + + sp<GraphicBuffer> overrideBuffer = + sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); + HwcSlotAndBuffer overrideSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(overrideBuffer); + // expect us to have a slot number + EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX); + // expect this to be the first time we cached the buffer + EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr); + + // expect the slot number to not equal any other slot number, even after the slots have been + // exhausted, indicating that the override buffer slot is independent from the slots for + // non-override buffers + for (int i = 0; i < finalCachedBufferIndex; ++i) { + EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot); + } + // the override buffer is independently uncached from the oldest cached buffer + // expect to find the override buffer still in the override buffer slot + EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot); + // expect that the first buffer was not evicted from the cache when the override buffer was + // cached + EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot); } -TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) { - testSlot(-123, 0); +TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1); + ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX); + + EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot); + EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX); +} + +TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) { + HwcBufferCache cache; + sp<GraphicBuffer> outBuffer; + + HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1); + ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX); + + EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index d7704a893d..33caa7a9b8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -70,6 +70,7 @@ public: MOCK_METHOD1(disconnectDisplay, void(HalDisplayId)); MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&)); MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId)); + MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override)); MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*)); MOCK_METHOD3(setOutputBuffer, status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&)); @@ -138,6 +139,8 @@ public: MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), (const, override)); MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); + MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, + getOverlaySupport, (), (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index c8bd5e436c..c555b39db1 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -34,11 +34,11 @@ public: MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected), (override)); MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override)); - MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override)); MOCK_METHOD(void, sendActualWorkDuration, (), (override)); MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); @@ -46,25 +46,24 @@ public: MOCK_METHOD(void, setGpuFenceTime, (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); MOCK_METHOD(void, setHwcValidateTiming, - (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime), (override)); MOCK_METHOD(void, setHwcPresentTiming, - (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); MOCK_METHOD(void, setRequiresClientComposition, (DisplayId displayId, bool requiresClientComposition), (override)); - MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); - MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime), + MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override)); + MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setHwcPresentDelayedTime, - (DisplayId displayId, - std::chrono::steady_clock::time_point earliestFrameStartTime)); - MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); - MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); - MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + (DisplayId displayId, TimePoint earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override)); MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); - MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 5290bd9cba..03cf2927af 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -774,7 +774,7 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72); static constexpr int kSupportedPerFrameMetadata = 101; static constexpr int kExpectedHwcSlot = 0; - static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT; + static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot; static constexpr bool kLayerGenericMetadata1Mandatory = true; static constexpr bool kLayerGenericMetadata2Mandatory = true; static constexpr float kWhitePointNits = 200.f; @@ -823,7 +823,6 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { mLayerFEState.hdrMetadata = kHdrMetadata; mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false); mLayerFEState.buffer = kBuffer; - mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT; mLayerFEState.acquireFence = kFence; mOutputState.displayBrightnessNits = kDisplayBrightnessNits; @@ -955,11 +954,11 @@ native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle = reinterpret_cast<native_handle_t*>(1031); const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer; const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = - new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888, - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN); + sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888, + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN); const sp<Fence> OutputLayerWriteStateToHWCTest::kFence; -const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence(); +const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = sp<Fence>::make(); const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key = "com.example.metadata.1"; const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}}; diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index cf12890310..bfd863b88a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -39,7 +39,6 @@ #include "CallOrderStateMachineHelper.h" #include "MockHWC2.h" #include "RegionMatcher.h" -#include "TestUtils.h" namespace android::compositionengine { namespace { @@ -293,7 +292,7 @@ TEST_F(OutputTest, setLayerCachingEnabled_disablesCachingAndResetsOverrideInfo) InjectedLayer layer; layer.outputLayerState.overrideInfo.buffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), renderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), renderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); injectOutputLayer(layer); @@ -759,56 +758,6 @@ TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) { } /* - * Output::updateLayerStateFromFE() - */ - -using OutputUpdateLayerStateFromFETest = OutputTest; - -TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) { - CompositionRefreshArgs refreshArgs; - - mOutput->updateLayerStateFromFE(refreshArgs); -} - -TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) { - InjectedLayer layer1; - InjectedLayer layer2; - InjectedLayer layer3; - - EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); - EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); - EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); - - injectOutputLayer(layer1); - injectOutputLayer(layer2); - injectOutputLayer(layer3); - - CompositionRefreshArgs refreshArgs; - refreshArgs.updatingGeometryThisFrame = false; - - mOutput->updateLayerStateFromFE(refreshArgs); -} - -TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) { - InjectedLayer layer1; - InjectedLayer layer2; - InjectedLayer layer3; - - EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); - EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); - EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); - - injectOutputLayer(layer1); - injectOutputLayer(layer2); - injectOutputLayer(layer3); - - CompositionRefreshArgs refreshArgs; - refreshArgs.updatingGeometryThisFrame = true; - - mOutput->updateLayerStateFromFE(refreshArgs); -} - -/* * Output::updateAndWriteCompositionState() */ @@ -1017,7 +966,7 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), renderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), renderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); layer1.outputLayerState.overrideInfo.buffer = buffer; @@ -1243,14 +1192,49 @@ struct OutputPrepareTest : public testing::Test { compositionengine::LayerFESet&)); }; + OutputPrepareTest() { + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0)) + .WillRepeatedly(Return(&mLayer1.outputLayer)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1)) + .WillRepeatedly(Return(&mLayer2.outputLayer)); + + mRefreshArgs.layers.push_back(mLayer1.layerFE); + mRefreshArgs.layers.push_back(mLayer2.layerFE); + } + + struct Layer { + StrictMock<mock::OutputLayer> outputLayer; + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); + }; + StrictMock<OutputPartialMock> mOutput; CompositionRefreshArgs mRefreshArgs; LayerFESet mGeomSnapshots; + Layer mLayer1; + Layer mLayer2; }; -TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) { +TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) { + InSequence seq; + + mRefreshArgs.bufferIdsToUncache = {1, 3, 5}; + + EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots))); + EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache))); + EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache))); + + mOutput.prepare(mRefreshArgs, mGeomSnapshots); +} + +TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) { InSequence seq; + + mRefreshArgs.bufferIdsToUncache = {}; + EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots))); + EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0); + EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0); mOutput.prepare(mRefreshArgs, mGeomSnapshots); } @@ -1537,9 +1521,6 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativ TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); - EXPECT_CALL(*mLayer.layerFE, - prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry)); - mGeomSnapshots.clear(); ensureOutputLayerIfVisible(); @@ -2135,6 +2116,7 @@ struct OutputUpdateColorProfileTest : public testing::Test { mOutput.setDisplayColorProfileForTest( std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); + mOutput.editState().isEnabled = true; EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0)) .WillRepeatedly(Return(&mLayer1.mOutputLayer)); @@ -3227,7 +3209,6 @@ TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCom // setup below are satisfied in the specific order. InSequence seq; - EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); @@ -3250,7 +3231,6 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence); frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence); - EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); @@ -3284,7 +3264,6 @@ TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make()); frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make()); - EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); @@ -3320,7 +3299,6 @@ TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { Output::FrameFences frameFences; frameFences.presentFence = presentFence; - EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); @@ -3389,8 +3367,7 @@ struct OutputComposeSurfacesTest : public testing::Test { EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine)); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); - EXPECT_CALL(mCompositionEngine, getTimeStats()) - .WillRepeatedly(ReturnRef(*mTimeStats.get())); + EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get())); EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()) .WillRepeatedly(ReturnRef(kHdrCapabilities)); } @@ -3449,7 +3426,7 @@ struct OutputComposeSurfacesTest : public testing::Test { StrictMock<OutputPartialMock> mOutput; std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), mRenderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); @@ -3531,9 +3508,8 @@ TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) { .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); verify().execute().expectAFenceWasReturned(); } @@ -3563,9 +3539,8 @@ TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); verify().execute().expectAFenceWasReturned(); @@ -3598,9 +3573,8 @@ TEST_F(OutputComposeSurfacesTest, .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); verify().execute().expectAFenceWasReturned(); @@ -3626,10 +3600,8 @@ TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithou EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) .Times(2) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))) + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); @@ -3657,8 +3629,7 @@ TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); verify().execute().expectAFenceWasReturned(); @@ -3687,7 +3658,7 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { const auto otherOutputBuffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), mRenderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) @@ -3697,9 +3668,8 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); verify().execute().expectAFenceWasReturned(); @@ -3730,11 +3700,9 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); @@ -3811,8 +3779,7 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> { auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) { EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _)) - .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); return nextState<ExecuteState>(); } }; @@ -4065,53 +4032,23 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) - .WillRepeatedly( - [&](const renderengine::DisplaySettings&, - const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { - return futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); - }); + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, + const bool, base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(Fence::NO_FENCE); + }); } Layer mLayer1; Layer mLayer2; }; -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) { - mOutput.mState.isSecure = false; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(false)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(false)); EXPECT_CALL(*mRenderSurface, setProtected(false)); base::unique_fd fd; @@ -4129,16 +4066,12 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { // For this test, we also check the call order of key functions. InSequence seq; - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, setProtected(true)); // Must happen after setting the protected content state. EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); base::unique_fd fd; std::shared_ptr<renderengine::ExternalTexture> tex; @@ -4151,7 +4084,6 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); base::unique_fd fd; @@ -4161,43 +4093,11 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); } -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false)); - EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true)); - EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(*mRenderSurface, setProtected(true)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); base::unique_fd fd; std::shared_ptr<renderengine::ExternalTexture> tex; @@ -4230,8 +4130,7 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDatas EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); base::unique_fd fd; std::shared_ptr<renderengine::ExternalTexture> tex; @@ -4254,8 +4153,7 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) - .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); @@ -4312,6 +4210,8 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { struct Layer { Layer() { + EXPECT_CALL(mOutputLayer, getOverrideCompositionSettings()) + .WillRepeatedly(Return(std::nullopt)); EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState)); EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState)); EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE)); @@ -4409,23 +4309,18 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionA } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) { - LayerFE::LayerSettings mShadowSettings; - mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; - - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings}))); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>( - {mShadowSettings, mLayers[2].mLayerSettings}))); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); - ASSERT_EQ(3u, requests.size()); + ASSERT_EQ(2u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); - EXPECT_EQ(mShadowSettings, requests[1]); - EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); + EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); @@ -4442,27 +4337,21 @@ MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "" } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) { - LayerFE::LayerSettings mShadowSettings; - mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; - mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings}))); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings))); EXPECT_CALL(*mLayers[2].mLayerFE, - prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq( + prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>( - {mShadowSettings, mLayers[2].mLayerSettings}))); - + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); - ASSERT_EQ(3u, requests.size()); + ASSERT_EQ(2u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); - EXPECT_EQ(mShadowSettings, requests[1]); - EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); + EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); @@ -4484,8 +4373,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mLayers[1].mLayerFEState.isOpaque = true; mLayers[2].mLayerFEState.isOpaque = true; - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); @@ -4507,8 +4396,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mLayers[1].mLayerFEState.isOpaque = false; mLayers[2].mLayerFEState.isOpaque = false; - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_)) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); @@ -4546,6 +4435,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu true /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4558,6 +4448,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings; @@ -4566,10 +4457,10 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu mBlackoutSettings.alpha = 0.f; mBlackoutSettings.disableBlending = true; - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings}))); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mBlackoutSettings))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); @@ -4598,6 +4489,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(Rect(0, 0, 30, 30)), @@ -4610,6 +4502,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(Rect(0, 0, 40, 201)), @@ -4622,14 +4515,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); static_cast<void>( mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, @@ -4652,6 +4546,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4664,6 +4559,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4676,14 +4572,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); static_cast<void>( mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, @@ -4706,6 +4603,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4718,6 +4616,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4730,14 +4629,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); static_cast<void>( mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, @@ -4759,6 +4659,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4771,6 +4672,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4783,14 +4685,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); static_cast<void>( mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, @@ -4810,6 +4713,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4822,6 +4726,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4834,14 +4739,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; - EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>())); static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */, kDisplayDataspace)); @@ -5018,12 +4924,13 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); - EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings}))); + EXPECT_CALL(*leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(leftLayer.mLayerSettings))); compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{ Region(Rect(1000, 0, 2000, 1000)), @@ -5036,12 +4943,13 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); - EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings}))); + EXPECT_CALL(*rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(rightLayer.mLayerSettings))); constexpr bool supportsProtectedContent = true; auto requests = @@ -5069,6 +4977,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; LayerFE::LayerSettings mShadowSettings; @@ -5079,8 +4988,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings}))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mShadowSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); @@ -5097,9 +5006,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, const Region kPartialContentWithPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80)); - LayerFE::LayerSettings mShadowSettings; - mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; - mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion; mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion; @@ -5114,20 +5020,19 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, kLayerWhitePointNits, + false /* treat170mAsSrgb */, }; EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); - EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings)))) - .WillOnce(Return(std::vector<LayerFE::LayerSettings>( - {mShadowSettings, mLayers[2].mLayerSettings}))); + EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings)))) + .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings))); auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, kDisplayDataspace); - ASSERT_EQ(2u, requests.size()); + ASSERT_EQ(1u, requests.size()); - EXPECT_EQ(mShadowSettings, requests[0]); - EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); + EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index e5f9ebfbeb..83937a679e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -61,8 +61,8 @@ public: StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; StrictMock<mock::Display> mDisplay; - sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>(); - sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>(); + sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make(); + sp<mock::DisplaySurface> mDisplaySurface = sp<StrictMock<mock::DisplaySurface>>::make(); impl::RenderSurface mSurface{mCompositionEngine, mDisplay, RenderSurfaceCreationArgsBuilder() .setDisplayWidth(DEFAULT_DISPLAY_WIDTH) @@ -109,7 +109,7 @@ TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) { */ TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) { - sp<Fence> fence = new Fence(); + sp<Fence> fence = sp<Fence>::make(); EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence)); @@ -234,7 +234,7 @@ TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) { */ TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) { - sp<GraphicBuffer> buffer = new GraphicBuffer(); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(); EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _)) .WillOnce( @@ -253,7 +253,7 @@ TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) { TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) { const auto buffer = std::make_shared< renderengine::impl:: - ExternalTexture>(new GraphicBuffer(), mRenderEngine, + ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine, renderengine::impl::ExternalTexture::Usage::READABLE | renderengine::impl::ExternalTexture::Usage::WRITEABLE); mSurface.mutableTextureForTest() = buffer; @@ -271,8 +271,9 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) { } TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) { - const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(), - mRenderEngine, false); + const auto buffer = + std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(), + mRenderEngine, false); mSurface.mutableTextureForTest() = buffer; impl::OutputCompositionState state; @@ -290,8 +291,9 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) { } TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) { - const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(), - mRenderEngine, false); + const auto buffer = + std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(), + mRenderEngine, false); mSurface.mutableTextureForTest() = buffer; impl::OutputCompositionState state; @@ -309,7 +311,7 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) { } TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) { - sp<GraphicBuffer> buffer = new GraphicBuffer(); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(); impl::OutputCompositionState state; state.usesClientComposition = false; @@ -329,8 +331,9 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferY } TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) { - const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(), - mRenderEngine, false); + const auto buffer = + std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(), + mRenderEngine, false); mSurface.mutableTextureForTest() = buffer; impl::OutputCompositionState state; @@ -359,17 +362,5 @@ TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) { mSurface.onPresentDisplayCompleted(); } -/* - * RenderSurface::flip() - */ - -TEST_F(RenderSurfaceTest, flipForwardsSignal) { - mSurface.setPageFlipCountForTest(500); - - mSurface.flip(); - - EXPECT_EQ(501u, mSurface.getPageFlipCount()); -} - } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 0e9db369e8..d5d688e705 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -28,8 +28,6 @@ #include <utils/Errors.h> #include <memory> -#include "tests/TestUtils.h" - namespace android::compositionengine { using namespace std::chrono_literals; @@ -345,19 +343,18 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - clientCompList1[0].alpha = 0.5f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + clientComp1->alpha = 0.5f; - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - clientCompList2[0].alpha = 0.75f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + clientComp2->alpha = 0.75f; - const auto drawLayers = - [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), @@ -365,15 +362,13 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { EXPECT_EQ(0.5f, layers[0].alpha); EXPECT_EQ(0.75f, layers[1].alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(*layerFE1, - prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false))) - .WillOnce(Return(clientCompList1)); - EXPECT_CALL(*layerFE2, - prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false))) - .WillOnce(Return(clientCompList2)); + EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false))) + .WillOnce(Return(clientComp1)); + EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false))) + .WillOnce(Return(clientComp2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = false; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); @@ -397,19 +392,18 @@ TEST_F(CachedSetTest, renderSecureOutput) { CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - clientCompList1[0].alpha = 0.5f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + clientComp1->alpha = 0.5f; - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - clientCompList2[0].alpha = 0.75f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + clientComp2->alpha = 0.75f; - const auto drawLayers = - [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), @@ -418,15 +412,13 @@ TEST_F(CachedSetTest, renderSecureOutput) { EXPECT_EQ(0.75f, layers[1].alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(*layerFE1, - prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true))) - .WillOnce(Return(clientCompList1)); - EXPECT_CALL(*layerFE2, - prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true))) - .WillOnce(Return(clientCompList2)); + EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true))) + .WillOnce(Return(clientComp1)); + EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true))) + .WillOnce(Return(clientComp2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); @@ -450,31 +442,30 @@ TEST_F(CachedSetTest, renderWhitePoint) { CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); mOutputState.displayBrightnessNits = 400.f; - const auto drawLayers = - [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; EXPECT_CALL(*layerFE1, - prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) - .WillOnce(Return(clientCompList1)); + .WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, - prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) - .WillOnce(Return(clientCompList2)); + .WillOnce(Return(clientComp2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); @@ -501,31 +492,30 @@ TEST_F(CachedSetTest, renderWhitePointNoColorTransform) { CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); mOutputState.displayBrightnessNits = 400.f; - const auto drawLayers = - [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; EXPECT_CALL(*layerFE1, - prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) - .WillOnce(Return(clientCompList1)); + .WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, - prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) - .WillOnce(Return(clientCompList2)); + .WillOnce(Return(clientComp2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false); @@ -549,21 +539,20 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - clientCompList1[0].alpha = 0.5f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + clientComp1->alpha = 0.5f; - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - clientCompList2[0].alpha = 0.75f; + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + clientComp2->alpha = 0.75f; mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5)); - const auto drawLayers = - [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), @@ -572,11 +561,11 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { EXPECT_EQ(0.75f, layers[1].alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); - EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); + EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); + EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); @@ -773,28 +762,27 @@ TEST_F(CachedSetTest, addHolePunch) { cachedSet.addHolePunchLayerIfFeasible(layer3, true); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3; - clientCompList3.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; + clientComp3.emplace(); - clientCompList3[0].source.buffer.buffer = + clientComp3->source.buffer.buffer = std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); - EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); - EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3)); + EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); + EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); + EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3)); - const auto drawLayers = - [&](const renderengine::DisplaySettings&, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 4u); @@ -814,7 +802,7 @@ TEST_F(CachedSetTest, addHolePunch) { EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha); } - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); @@ -822,7 +810,7 @@ TEST_F(CachedSetTest, addHolePunch) { } TEST_F(CachedSetTest, addHolePunch_noBuffer) { - // Same as addHolePunch, except that clientCompList3 does not contain a + // Same as addHolePunch, except that clientComp3 does not contain a // buffer. This imitates the case where the buffer had protected content, so // BufferLayer did not add it to the LayerSettings. This should not assert. mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); @@ -840,22 +828,21 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { cachedSet.addHolePunchLayerIfFeasible(layer3, true); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3; - clientCompList3.push_back({}); - - EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); - EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); - EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3)); - - const auto drawLayers = - [&](const renderengine::DisplaySettings&, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; + clientComp3.emplace(); + + EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); + EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); + EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3)); + + const auto drawLayers = [&](const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 4u); @@ -876,7 +863,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha); } - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); @@ -974,40 +961,39 @@ TEST_F(CachedSetTest, addBlur) { cachedSet.addBackgroundBlurLayer(layer3); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; - clientCompList1.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; - clientCompList2.push_back({}); - std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3; - clientCompList3.push_back({}); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; + clientComp1.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; + clientComp2.emplace(); + std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; + clientComp3.emplace(); - clientCompList3[0].source.buffer.buffer = + clientComp3->source.buffer.buffer = std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); EXPECT_CALL(*layerFE1, - prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq( + prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: Enabled))) - .WillOnce(Return(clientCompList1)); + .WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, - prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq( + prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: Enabled))) - .WillOnce(Return(clientCompList2)); + .WillOnce(Return(clientComp2)); EXPECT_CALL(*layerFE3, - prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq( + prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: BackgroundBlurOnly))) - .WillOnce(Return(clientCompList3)); + .WillOnce(Return(clientComp3)); - const auto drawLayers = - [&](const renderengine::DisplaySettings&, - const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + const auto drawLayers = [&](const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 3u); @@ -1016,7 +1002,7 @@ TEST_F(CachedSetTest, addBlur) { EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor); EXPECT_EQ(0.0f, blurSettings.alpha); - return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 96021ec104..86cfee6f0a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -27,8 +27,6 @@ #include <renderengine/mock/RenderEngine.h> #include <chrono> -#include "tests/TestUtils.h" - namespace android::compositionengine { using namespace std::chrono_literals; using impl::planner::CachedSet; @@ -108,11 +106,12 @@ void FlattenerTest::SetUp() { testLayer->outputLayerCompositionState.visibleRegion = Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2)); + const auto kUsageFlags = + static_cast<uint64_t>(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE); testLayer->layerFECompositionState.buffer = - new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, - "output"); + sp<GraphicBuffer>::make(100u, 100u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, + "output"); testLayer->layerFE = sp<mock::LayerFE>::make(); @@ -123,12 +122,11 @@ void FlattenerTest::SetUp() { EXPECT_CALL(*testLayer->layerFE, getCompositionState) .WillRepeatedly(Return(&testLayer->layerFECompositionState)); - std::vector<LayerFE::LayerSettings> clientCompositionList = { - LayerFE::LayerSettings{}, - }; + std::optional<LayerFE::LayerSettings> clientComposition; + clientComposition.emplace(); - EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList) - .WillRepeatedly(Return(clientCompositionList)); + EXPECT_CALL(*testLayer->layerFE, prepareClientComposition) + .WillRepeatedly(Return(clientComposition)); EXPECT_CALL(testLayer->outputLayer, getLayerFE) .WillRepeatedly(ReturnRef(*testLayer->layerFE)); EXPECT_CALL(testLayer->outputLayer, getState) @@ -171,8 +169,7 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) { // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -423,8 +420,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { layerState1->resetFramesSinceBufferUpdate(); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -447,8 +443,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { mTime += 200ms; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -500,8 +495,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { layerState3->resetFramesSinceBufferUpdate(); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -515,8 +509,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5 EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -545,8 +538,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { layerState3->incrementFramesSinceBufferUpdate(); mTime += 200ms; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -601,8 +593,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -637,16 +628,15 @@ TEST_F(FlattenerTest, flattenLayers_pip) { EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); - std::vector<LayerFE::LayerSettings> clientCompositionList = { - LayerFE::LayerSettings{}, - }; - clientCompositionList[0].source.buffer.buffer = std::make_shared< + std::optional<LayerFE::LayerSettings> clientComposition; + clientComposition.emplace(); + clientComposition->source.buffer.buffer = std::make_shared< renderengine::impl::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer, mRenderEngine, renderengine::impl::ExternalTexture::Usage:: READABLE); - EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_)) - .WillOnce(Return(clientCompositionList)); + EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientComposition(_)) + .WillOnce(Return(clientComposition)); const std::vector<const LayerState*> layers = { layerState1.get(), @@ -667,8 +657,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -711,16 +700,15 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); - std::vector<LayerFE::LayerSettings> clientCompositionList = { - LayerFE::LayerSettings{}, - }; - clientCompositionList[0].source.buffer.buffer = std::make_shared< + std::optional<LayerFE::LayerSettings> clientComposition; + clientComposition.emplace(); + clientComposition->source.buffer.buffer = std::make_shared< renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer, mRenderEngine, renderengine::impl::ExternalTexture::Usage:: READABLE); - EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_)) - .WillOnce(Return(clientCompositionList)); + EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_)) + .WillOnce(Return(clientComposition)); const std::vector<const LayerState*> layers = { layerState0.get(), @@ -741,8 +729,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the // exception that there would be a hole punch above it. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -783,16 +770,15 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); - std::vector<LayerFE::LayerSettings> clientCompositionList = { - LayerFE::LayerSettings{}, - }; - clientCompositionList[0].source.buffer.buffer = std::make_shared< + std::optional<LayerFE::LayerSettings> clientComposition; + clientComposition.emplace(); + clientComposition->source.buffer.buffer = std::make_shared< renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer, mRenderEngine, renderengine::impl::ExternalTexture::Usage:: READABLE); - EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_)) - .WillOnce(Return(clientCompositionList)); + EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_)) + .WillOnce(Return(clientComposition)); const std::vector<const LayerState*> layers = { layerState0.get(), @@ -813,8 +799,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the // exception that there would be a hole punch above it. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -865,8 +850,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -911,8 +895,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillRepeatedly(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillRepeatedly(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -965,8 +948,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -1014,8 +996,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -1057,8 +1038,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { mTime += 200ms; // layers would be flattened but the buffer would not be overridden EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -1125,8 +1105,7 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM } EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - (kCachedSetRenderDuration + 10ms), @@ -1162,8 +1141,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -1213,8 +1191,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -1264,8 +1241,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -1318,8 +1294,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. @@ -1371,8 +1346,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) - .WillOnce(Return(ByMove( - futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp index 5c6e8da58c..47b682059f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp @@ -112,6 +112,9 @@ struct LayerStateTest : public testing::Test { sp<mock::LayerFE> mLayerFE = sp<mock::LayerFE>::make(); mock::OutputLayer mOutputLayer; std::unique_ptr<LayerState> mLayerState; + + static constexpr auto kUsageFlags = static_cast<uint32_t>( + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN); }; TEST_F(LayerStateTest, getOutputLayer) { @@ -346,7 +349,7 @@ TEST_F(LayerStateTest, compareCompositionType) { TEST_F(LayerStateTest, updateBuffer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - layerFECompositionState.buffer = new GraphicBuffer(); + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -354,7 +357,7 @@ TEST_F(LayerStateTest, updateBuffer) { mock::OutputLayer newOutputLayer; sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); @@ -364,7 +367,7 @@ TEST_F(LayerStateTest, updateBuffer) { TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - layerFECompositionState.buffer = new GraphicBuffer(); + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -372,7 +375,7 @@ TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) { mock::OutputLayer newOutputLayer; sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); @@ -388,7 +391,7 @@ TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) { TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - layerFECompositionState.buffer = new GraphicBuffer(); + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -396,7 +399,7 @@ TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) { mock::OutputLayer newOutputLayer; sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); layerFECompositionStateTwo.buffer->usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); @@ -412,14 +415,14 @@ TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) { TEST_F(LayerStateTest, compareBuffer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - layerFECompositionState.buffer = new GraphicBuffer(); + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -720,10 +723,7 @@ TEST_F(LayerStateTest, updatePixelFormat) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.buffer = - new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, - "buffer1"); + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, kUsageFlags, "buffer1"); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -732,10 +732,7 @@ TEST_F(LayerStateTest, updatePixelFormat) { sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = - new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888, - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, - "buffer2"); + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBX_8888, kUsageFlags, "buffer2"); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); @@ -748,10 +745,7 @@ TEST_F(LayerStateTest, comparePixelFormat) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.buffer = - new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, - "buffer1"); + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, kUsageFlags, "buffer1"); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -759,10 +753,7 @@ TEST_F(LayerStateTest, comparePixelFormat) { sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = - new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888, - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, - "buffer2"); + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBX_8888, kUsageFlags, "buffer2"); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -1069,7 +1060,7 @@ TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) { TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - layerFECompositionState.buffer = new GraphicBuffer(); + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -1077,7 +1068,7 @@ TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) { mock::OutputLayer newOutputLayer; sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp index 68c72e0945..35d0ffb6e9 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp @@ -228,7 +228,7 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { } TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) { - sp<GraphicBuffer> buffer = new GraphicBuffer(); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(); mock::OutputLayer outputLayerOne; sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne; @@ -268,7 +268,7 @@ TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { .dataspace = ui::Dataspace::SRGB, }; LayerFECompositionState layerFECompositionStateOne; - layerFECompositionStateOne.buffer = new GraphicBuffer(); + layerFECompositionStateOne.buffer = sp<GraphicBuffer>::make(); layerFECompositionStateOne.alpha = sAlphaOne; layerFECompositionStateOne.colorTransformIsIdentity = true; setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, @@ -285,7 +285,7 @@ TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { .dataspace = ui::Dataspace::DISPLAY_P3, }; LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); layerFECompositionStateTwo.alpha = sAlphaTwo; layerFECompositionStateTwo.colorTransformIsIdentity = false; layerFECompositionStateTwo.colorTransform = sMat4One; @@ -310,7 +310,7 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) { .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - layerFECompositionStateOne.buffer = new GraphicBuffer(); + layerFECompositionStateOne.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); @@ -321,7 +321,7 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) { .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make(); setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp deleted file mode 100644 index 3ccc229261..0000000000 --- a/services/surfaceflinger/ContainerLayer.cpp +++ /dev/null @@ -1,47 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -// #define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "ContainerLayer" - -#include "ContainerLayer.h" - -namespace android { - -ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {} - -ContainerLayer::~ContainerLayer() = default; - -bool ContainerLayer::isVisible() const { - return false; -} - -sp<Layer> ContainerLayer::createClone() { - sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer( - LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata())); - layer->setInitialValuesForClone(this); - return layer; -} - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Display/DisplayMap.h b/services/surfaceflinger/Display/DisplayMap.h new file mode 100644 index 0000000000..0d5970676e --- /dev/null +++ b/services/surfaceflinger/Display/DisplayMap.h @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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 <ftl/small_map.h> +#include <ftl/small_vector.h> + +namespace android::display { + +// The static capacities were chosen to exceed a typical number of physical and/or virtual displays. + +template <typename Key, typename Value> +using DisplayMap = ftl::SmallMap<Key, Value, 5>; + +template <typename Key, typename Value> +using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>; + +template <typename T> +using PhysicalDisplayVector = ftl::SmallVector<T, 3>; + +} // namespace android::display diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h new file mode 100644 index 0000000000..d07cdf55d2 --- /dev/null +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -0,0 +1,36 @@ +/* + * Copyright 2022 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 <ftl/non_null.h> + +#include <scheduler/FrameRateMode.h> + +namespace android::display { + +struct DisplayModeRequest { + scheduler::FrameRateMode mode; + + // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. + bool emitEvent = false; +}; + +inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { + return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent; +} + +} // namespace android::display diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp new file mode 100644 index 0000000000..0c7a58ee71 --- /dev/null +++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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 <algorithm> +#include <functional> +#include <utility> + +#include <ftl/algorithm.h> +#include <ftl/enum.h> +#include <ui/DebugUtils.h> + +#include "DisplaySnapshot.h" + +namespace android::display { + +DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId, + ui::DisplayConnectionType connectionType, + DisplayModes&& displayModes, ui::ColorModes&& colorModes, + std::optional<DeviceProductInfo>&& deviceProductInfo) + : mDisplayId(displayId), + mConnectionType(connectionType), + mDisplayModes(std::move(displayModes)), + mColorModes(std::move(colorModes)), + mDeviceProductInfo(std::move(deviceProductInfo)) {} + +std::optional<DisplayModeId> DisplaySnapshot::translateModeId(hal::HWConfigId hwcId) const { + return ftl::find_if(mDisplayModes, + [hwcId](const DisplayModes::value_type& pair) { + return pair.second->getHwcId() == hwcId; + }) + .transform(&ftl::to_key<DisplayModes>); +} + +ui::ColorModes DisplaySnapshot::filterColorModes(bool supportsWideColor) const { + ui::ColorModes modes = mColorModes; + + // If the display is internal and the configuration claims it's not wide color capable, filter + // out all wide color modes. The typical reason why this happens is that the hardware is not + // good enough to support GPU composition of wide color, and thus the OEMs choose to disable + // this capability. + if (mConnectionType == ui::DisplayConnectionType::Internal && !supportsWideColor) { + const auto it = std::remove_if(modes.begin(), modes.end(), ui::isWideColorMode); + modes.erase(it, modes.end()); + } + + return modes; +} + +void DisplaySnapshot::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; + + dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType)); + + dumper.dump("colorModes"sv); + { + utils::Dumper::Indent indent(dumper); + for (const auto mode : mColorModes) { + dumper.dump({}, decodeColorMode(mode)); + } + } + + dumper.dump("deviceProductInfo"sv, mDeviceProductInfo); +} + +} // namespace android::display diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h new file mode 100644 index 0000000000..23471f5d8e --- /dev/null +++ b/services/surfaceflinger/Display/DisplaySnapshot.h @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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 <optional> + +#include <ui/ColorMode.h> +#include <ui/DisplayId.h> +#include <ui/StaticDisplayInfo.h> + +#include "DisplayHardware/DisplayMode.h" +#include "Utils/Dumper.h" + +namespace android::display { + +// Immutable state of a physical display, captured on hotplug. +class DisplaySnapshot { +public: + DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&, + std::optional<DeviceProductInfo>&&); + + DisplaySnapshot(const DisplaySnapshot&) = delete; + DisplaySnapshot(DisplaySnapshot&&) = default; + + PhysicalDisplayId displayId() const { return mDisplayId; } + ui::DisplayConnectionType connectionType() const { return mConnectionType; } + + std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const; + + const auto& displayModes() const { return mDisplayModes; } + const auto& colorModes() const { return mColorModes; } + const auto& deviceProductInfo() const { return mDeviceProductInfo; } + + ui::ColorModes filterColorModes(bool supportsWideColor) const; + + void dump(utils::Dumper&) const; + +private: + const PhysicalDisplayId mDisplayId; + const ui::DisplayConnectionType mConnectionType; + + // Effectively const except in move constructor. + DisplayModes mDisplayModes; + ui::ColorModes mColorModes; + std::optional<DeviceProductInfo> mDeviceProductInfo; +}; + +} // namespace android::display diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h new file mode 100644 index 0000000000..cba10146b7 --- /dev/null +++ b/services/surfaceflinger/Display/PhysicalDisplay.h @@ -0,0 +1,76 @@ +/* + * Copyright 2022 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 <functional> +#include <utility> + +#include <binder/IBinder.h> +#include <ui/DisplayId.h> +#include <utils/StrongPointer.h> + +#include "DisplayMap.h" +#include "DisplaySnapshot.h" + +namespace android::display { + +// TODO(b/229877597): Replace with AIDL type. +using DisplayToken = IBinder; + +class PhysicalDisplay { +public: + template <typename... Args> + PhysicalDisplay(sp<DisplayToken> token, Args&&... args) + : mToken(std::move(token)), mSnapshot(std::forward<Args>(args)...) {} + + PhysicalDisplay(const PhysicalDisplay&) = delete; + PhysicalDisplay(PhysicalDisplay&&) = default; + + const sp<DisplayToken>& token() const { return mToken; } + const DisplaySnapshot& snapshot() const { return mSnapshot; } + + // Transformers for PhysicalDisplays::get. + + using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>; + SnapshotRef snapshotRef() const { return std::cref(mSnapshot); } + + bool isInternal() const { + return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal; + } + + // Predicate for ftl::find_if on PhysicalDisplays. + static constexpr auto hasToken(const sp<DisplayToken>& token) { + return [&token](const std::pair<const PhysicalDisplayId, PhysicalDisplay>& pair) { + return pair.second.token() == token; + }; + } + +private: + const sp<DisplayToken> mToken; + + // Effectively const except in move constructor. + DisplaySnapshot mSnapshot; +}; + +using PhysicalDisplays = PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>; + +// Combinator for ftl::Optional<PhysicalDisplayId>::and_then. +constexpr auto getPhysicalDisplay(const PhysicalDisplays& displays) { + return [&](PhysicalDisplayId id) { return displays.get(id); }; +} + +} // namespace android::display diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 26fbd55d6e..46b857b189 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -39,7 +39,9 @@ #include <system/window.h> #include <ui/GraphicTypes.h> +#include "Display/DisplaySnapshot.h" #include "DisplayDevice.h" +#include "FrontEnd/DisplayInfo.h" #include "Layer.h" #include "RefreshRateOverlay.h" #include "SurfaceFlinger.h" @@ -63,14 +65,13 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mHwComposer(args.hwComposer), mDisplayToken(args.displayToken), mSequenceId(args.sequenceId), - mConnectionType(args.connectionType), mCompositionDisplay{args.compositionDisplay}, mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())), mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())), + mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())), mPhysicalOrientation(args.physicalOrientation), - mSupportedModes(std::move(args.supportedModes)), mIsPrimary(args.isPrimary), - mRefreshRateConfigs(std::move(args.refreshRateConfigs)) { + mRefreshRateSelector(std::move(args.refreshRateSelector)) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgsBuilder() @@ -104,7 +105,9 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay->getRenderSurface()->initialize(); - if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value()); + if (const auto powerModeOpt = args.initialPowerMode) { + setPowerMode(*powerModeOpt); + } // initialize the display orientation transform. setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); @@ -132,15 +135,7 @@ void DisplayDevice::setDisplayName(const std::string& displayName) { } } -void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) { - mDeviceProductInfo = std::move(info); -} - -uint32_t DisplayDevice::getPageFlipCount() const { - return mCompositionDisplay->getRenderSurface()->getPageFlipCount(); -} - -auto DisplayDevice::getInputInfo() const -> InputInfo { +auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { gui::DisplayInfo info; info.displayId = getLayerStack().id; @@ -169,7 +164,9 @@ auto DisplayDevice::getInputInfo() const -> InputInfo { return {.info = info, .transform = displayTransform, .receivesInput = receivesInput(), - .isSecure = isSecure()}; + .isSecure = isSecure(), + .isPrimary = isPrimary(), + .rotationFlags = ui::Transform::toRotationFlags(mOrientation)}; } void DisplayDevice::setPowerMode(hal::PowerMode mode) { @@ -184,8 +181,7 @@ void DisplayDevice::setPowerMode(hal::PowerMode mode) { mPowerMode = mode; - getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() && - *mPowerMode != hal::PowerMode::OFF); + getCompositionDisplay()->setCompositionEnabled(isPoweredOn()); } void DisplayDevice::enableLayerCaching(bool enable) { @@ -200,56 +196,32 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode && *mPowerMode != hal::PowerMode::OFF; } -void DisplayDevice::setActiveMode(DisplayModeId id) { - const auto mode = getMode(id); - LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported."); - ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue()); - mActiveMode = mode; - if (mRefreshRateConfigs) { - mRefreshRateConfigs->setActiveModeId(mActiveMode->getId()); - } +void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) { + ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue()); + ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue()); + + mRefreshRateSelector->setActiveMode(modeId, renderFps); + if (mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps()); + mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps); } } status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline* outTimeline) { - if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) { + if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) { ALOGE("Trying to initiate a mode change to invalid mode %s on display %s", - info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null", + info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str() + : "null", to_string(getId()).c_str()); return BAD_VALUE; } - mNumModeSwitchesInPolicy++; mUpcomingActiveMode = info; - ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue()); - return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(), - constraints, outTimeline); -} - -const DisplayModePtr& DisplayDevice::getActiveMode() const { - return mActiveMode; -} - -const DisplayModes& DisplayDevice::getSupportedModes() const { - return mSupportedModes; -} - -DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const { - const DisplayModePtr nullMode; - return mSupportedModes.get(modeId).value_or(std::cref(nullMode)); -} - -std::optional<DisplayModeId> DisplayDevice::translateModeId(hal::HWConfigId hwcId) const { - const auto it = - std::find_if(mSupportedModes.begin(), mSupportedModes.end(), - [hwcId](const auto& pair) { return pair.second->getHwcId() == hwcId; }); - if (it != mSupportedModes.end()) { - return it->second->getId(); - } - return {}; + ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue()); + return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), + info.modeOpt->modePtr->getHwcId(), constraints, + outTimeline); } nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { @@ -264,27 +236,17 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return vsyncPeriod; } - return getActiveMode()->getFps().getPeriodNsecs(); -} - -nsecs_t DisplayDevice::getRefreshTimestamp() const { - const nsecs_t now = systemTime(CLOCK_MONOTONIC); - const auto vsyncPeriodNanos = getVsyncPeriodFromHWC(); - return now - ((now - mLastHwVsync) % vsyncPeriodNanos); -} - -void DisplayDevice::onVsync(nsecs_t timestamp) { - mLastHwVsync = timestamp; + return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod(); } ui::Dataspace DisplayDevice::getCompositionDataSpace() const { return mCompositionDisplay->getState().dataspace; } -void DisplayDevice::setLayerStack(ui::LayerStack stack) { - mCompositionDisplay->setLayerFilter({stack, isInternal()}); +void DisplayDevice::setLayerFilter(ui::LayerFilter filter) { + mCompositionDisplay->setLayerFilter(filter); if (mRefreshRateOverlay) { - mRefreshRateOverlay->setLayerStack(stack); + mRefreshRateOverlay->setLayerStack(filter.layerStack); } } @@ -353,44 +315,14 @@ ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { return sPrimaryDisplayRotationFlags; } -std::string DisplayDevice::getDebugName() const { - using namespace std::string_literals; +void DisplayDevice::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; - std::string name = "Display "s + to_string(getId()) + " ("s; + dumper.dump("name"sv, '"' + mDisplayName + '"'); + dumper.dump("powerMode"sv, mPowerMode); - if (mConnectionType) { - name += isInternal() ? "internal"s : "external"s; - } else { - name += "virtual"s; - } - - if (isPrimary()) { - name += ", primary"s; - } - - return name + ", \""s + mDisplayName + "\")"s; -} - -void DisplayDevice::dump(std::string& result) const { - using namespace std::string_literals; - - result += getDebugName(); - - if (!isVirtual()) { - result += "\n deviceProductInfo="s; - if (mDeviceProductInfo) { - mDeviceProductInfo->dump(result); - } else { - result += "{}"s; - } - } - - result += "\n powerMode="s; - result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)"; - result += '\n'; - - if (mRefreshRateConfigs) { - mRefreshRateConfigs->dump(result); + if (mRefreshRateSelector) { + mRefreshRateSelector->dump(dumper); } } @@ -478,26 +410,35 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } -void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { +void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) { if (!enable) { mRefreshRateOverlay.reset(); return; } - const auto fpsRange = mRefreshRateConfigs->getSupportedRefreshRateRange(); - mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner); + ftl::Flags<RefreshRateOverlay::Features> features; + if (showSpinner) { + features |= RefreshRateOverlay::Features::Spinner; + } + + if (showRenderRate) { + features |= RefreshRateOverlay::Features::RenderRate; + } + + const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); + mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features); mRefreshRateOverlay->setLayerStack(getLayerStack()); mRefreshRateOverlay->setViewport(getSize()); - mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps()); + mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps); } bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId, bool timerExpired) { - if (mRefreshRateConfigs && mRefreshRateOverlay) { - const auto newRefreshRate = - mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired); - if (newRefreshRate) { - mRefreshRateOverlay->changeRefreshRate(*newRefreshRate); + if (mRefreshRateSelector && mRefreshRateOverlay) { + const auto newMode = + mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired); + if (newMode) { + mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps); return true; } } @@ -511,13 +452,14 @@ void DisplayDevice::animateRefreshRateOverlay() { } } -bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { +auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction { ATRACE_CALL(); - LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided"); - LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch"); + LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided"); + LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(), + "DisplayId mismatch"); - ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str()); + ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str()); std::scoped_lock lock(mActiveModeLock); if (mDesiredActiveModeChanged) { @@ -525,18 +467,25 @@ bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { const auto prevConfig = mDesiredActiveMode.event; mDesiredActiveMode = info; mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig; - return false; + return DesiredActiveModeAction::None; } + const auto& desiredMode = *info.modeOpt->modePtr; + // Check if we are already at the desired mode - if (getActiveMode()->getId() == info.mode->getId()) { - return false; + if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) { + if (refreshRateSelector().getActiveMode() == info.modeOpt) { + return DesiredActiveModeAction::None; + } + + setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps); + return DesiredActiveModeAction::InitiateRenderRateSwitch; } // Initiate a mode change. mDesiredActiveModeChanged = true; mDesiredActiveMode = info; - return true; + return DesiredActiveModeAction::InitiateDisplayModeSwitch; } std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const { @@ -551,27 +500,6 @@ void DisplayDevice::clearDesiredActiveModeState() { mDesiredActiveModeChanged = false; } -status_t DisplayDevice::setRefreshRatePolicy( - const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) { - const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy(); - const status_t setPolicyResult = overridePolicy - ? mRefreshRateConfigs->setOverridePolicy(policy) - : mRefreshRateConfigs->setDisplayManagerPolicy(*policy); - - if (setPolicyResult == OK) { - const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0); - - ALOGI("Display %s policy changed\n" - "Previous: {%s}\n" - "Current: {%s}\n" - "%d mode changes were performed under the previous policy", - to_string(getId()).c_str(), oldPolicy.toString().c_str(), - policy ? policy->toString().c_str() : "null", numModeChanges); - } - - return setPolicyResult; -} - std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index fc24a9ce49..8f9b2a19a6 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -41,13 +41,15 @@ #include <utils/RefBase.h> #include <utils/Timers.h> +#include "Display/DisplayModeRequest.h" #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/PowerAdvisor.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "FrontEnd/DisplayInfo.h" +#include "Scheduler/RefreshRateSelector.h" #include "ThreadContext.h" #include "TracedOrdinal.h" - +#include "Utils/Dumper.h" namespace android { class Fence; @@ -65,6 +67,10 @@ class Display; class DisplaySurface; } // namespace compositionengine +namespace display { +class DisplaySnapshot; +} // namespace display + class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; @@ -80,11 +86,8 @@ public: return mCompositionDisplay; } - std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; } - - bool isVirtual() const { return !mConnectionType; } + bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); } bool isPrimary() const { return mIsPrimary; } - bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; } // isSecure indicates whether this display can be trusted to display // secure surfaces. @@ -94,7 +97,7 @@ public: int getHeight() const; ui::Size getSize() const { return {getWidth(), getHeight()}; } - void setLayerStack(ui::LayerStack); + void setLayerFilter(ui::LayerFilter); void setDisplaySize(int width, int height); void setProjection(ui::Rotation orientation, Rect viewport, Rect frame); void stageBrightness(float brightness) REQUIRES(kMainThreadContext); @@ -164,19 +167,7 @@ public: void setDisplayName(const std::string& displayName); const std::string& getDisplayName() const { return mDisplayName; } - void setDeviceProductInfo(std::optional<DeviceProductInfo> info); - const std::optional<DeviceProductInfo>& getDeviceProductInfo() const { - return mDeviceProductInfo; - } - - struct InputInfo { - gui::DisplayInfo info; - ui::Transform transform; - bool receivesInput; - bool isSecure; - }; - - InputInfo getInputInfo() const; + surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const; /* ------------------------------------------------------------------------ * Display power mode management. @@ -193,113 +184,103 @@ public: /* ------------------------------------------------------------------------ * Display mode management. */ - const DisplayModePtr& getActiveMode() const; + // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest. struct ActiveModeInfo { - DisplayModePtr mode; - scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None; + using Event = scheduler::DisplayModeEvent; + + ActiveModeInfo() = default; + ActiveModeInfo(scheduler::FrameRateMode mode, Event event) + : modeOpt(std::move(mode)), event(event) {} + + explicit ActiveModeInfo(display::DisplayModeRequest&& request) + : ActiveModeInfo(std::move(request.mode), + request.emitEvent ? Event::Changed : Event::None) {} + + ftl::Optional<scheduler::FrameRateMode> modeOpt; + Event event = Event::None; bool operator!=(const ActiveModeInfo& other) const { - return mode != other.mode || event != other.event; + return modeOpt != other.modeOpt || event != other.event; } }; - bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock); + enum class DesiredActiveModeAction { + None, + InitiateDisplayModeSwitch, + InitiateRenderRateSwitch + }; + DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock); std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock); void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock); ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) { return mUpcomingActiveMode; } - void setActiveMode(DisplayModeId) REQUIRES(kMainThreadContext); + scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) { + return mRefreshRateSelector->getActiveMode(); + } + + void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps); + status_t initiateModeChange(const ActiveModeInfo&, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline* outTimeline) REQUIRES(kMainThreadContext); - // Return the immutable list of supported display modes. The HWC may report different modes - // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated. - // Hotplug reconnects are common for external displays. - const DisplayModes& getSupportedModes() const; - - // Returns nullptr if the given mode ID is not supported. A previously - // supported mode may be no longer supported for some devices like TVs and - // set-top boxes after a hotplug reconnect. - DisplayModePtr getMode(DisplayModeId) const; - - std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const; + scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; } - // Returns the refresh rate configs for this display. - scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; } - - // Returns a shared pointer to the refresh rate configs for this display. - // Clients can store this refresh rate configs and use it even if the DisplayDevice - // is destroyed. - std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const { - return mRefreshRateConfigs; + // Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice. + std::shared_ptr<scheduler::RefreshRateSelector> holdRefreshRateSelector() const { + return mRefreshRateSelector; } // Enables an overlay to be displayed with the current refresh rate - void enableRefreshRateOverlay(bool enable, bool showSpinner); + void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) + REQUIRES(kMainThreadContext); bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); void animateRefreshRateOverlay(); - void onVsync(nsecs_t timestamp); nsecs_t getVsyncPeriodFromHWC() const; - nsecs_t getRefreshTimestamp() const; - - status_t setRefreshRatePolicy( - const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, - bool overridePolicy); // release HWC resources (if any) for removable displays void disconnect(); - /* ------------------------------------------------------------------------ - * Debugging - */ - uint32_t getPageFlipCount() const; - std::string getDebugName() const; - void dump(std::string& result) const; + void dump(utils::Dumper&) const; private: const sp<SurfaceFlinger> mFlinger; HWComposer& mHwComposer; const wp<IBinder> mDisplayToken; const int32_t mSequenceId; - const std::optional<ui::DisplayConnectionType> mConnectionType; const std::shared_ptr<compositionengine::Display> mCompositionDisplay; std::string mDisplayName; std::string mActiveModeFPSTrace; std::string mActiveModeFPSHwcTrace; + std::string mRenderFrameRateFPSTrace; const ui::Rotation mPhysicalOrientation; ui::Rotation mOrientation = ui::ROTATION_0; static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags; - // allow initial power mode as null. + // Allow nullopt as initial power mode. std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode; - DisplayModePtr mActiveMode; - std::optional<float> mStagedBrightness = std::nullopt; - float mBrightness = -1.f; - const DisplayModes mSupportedModes; - std::atomic<nsecs_t> mLastHwVsync = 0; + std::optional<float> mStagedBrightness; + float mBrightness = -1.f; // TODO(b/182939859): Remove special cases for primary display. const bool mIsPrimary; uint32_t mFlags = 0; - std::optional<DeviceProductInfo> mDeviceProductInfo; - std::vector<ui::Hdr> mOverrideHdrTypes; - std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay; mutable std::mutex mActiveModeLock; @@ -307,21 +288,16 @@ private: TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false}; ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext); - - std::atomic_int mNumModeSwitchesInPolicy = 0; }; struct DisplayDeviceState { struct Physical { PhysicalDisplayId id; - ui::DisplayConnectionType type; hardware::graphics::composer::hal::HWDisplayId hwcDisplayId; - std::optional<DeviceProductInfo> deviceProductInfo; - DisplayModes supportedModes; DisplayModePtr activeMode; bool operator==(const Physical& other) const { - return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId; + return id == other.id && hwcDisplayId == other.hwcDisplayId; } }; @@ -354,10 +330,9 @@ struct DisplayDeviceCreationArgs { HWComposer& hwComposer; const wp<IBinder> displayToken; const std::shared_ptr<compositionengine::Display> compositionDisplay; - std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> refreshRateSelector; int32_t sequenceId{0}; - std::optional<ui::DisplayConnectionType> connectionType; bool isSecure{false}; sp<ANativeWindow> nativeWindow; sp<compositionengine::DisplaySurface> displaySurface; @@ -368,7 +343,6 @@ struct DisplayDeviceCreationArgs { std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode; bool isPrimary{false}; - DisplayModes supportedModes; DisplayModeId activeModeId; }; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 36512311d1..800e36db48 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -55,6 +55,7 @@ using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::D using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute; using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability; using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities; +using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties; using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata; using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey; using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob; @@ -229,6 +230,8 @@ AidlComposer::AidlComposer(const std::string& serviceName) { return; } + addReader(translate<Display>(kSingleReaderKey)); + ALOGI("Loaded AIDL composer3 HAL service"); } @@ -297,12 +300,19 @@ void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { } } -void AidlComposer::resetCommands() { - mWriter.reset(); +void AidlComposer::resetCommands(Display display) { + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().reset(); + } + mMutex.unlock_shared(); } -Error AidlComposer::executeCommands() { - return execute(); +Error AidlComposer::executeCommands(Display display) { + mMutex.lock_shared(); + auto error = execute(display); + mMutex.unlock_shared(); + return error; } uint32_t AidlComposer::getMaxVirtualDisplayCount() { @@ -333,6 +343,7 @@ Error AidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelF *outDisplay = translate<Display>(virtualDisplay.display); *format = static_cast<PixelFormat>(virtualDisplay.format); + addDisplay(translate<Display>(virtualDisplay.display)); return Error::NONE; } @@ -342,12 +353,20 @@ Error AidlComposer::destroyVirtualDisplay(Display display) { ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str()); return static_cast<Error>(status.getServiceSpecificError()); } + removeDisplay(display); return Error::NONE; } Error AidlComposer::acceptDisplayChanges(Display display) { - mWriter.acceptDisplayChanges(translate<int64_t>(display)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().acceptDisplayChanges(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::createLayer(Display display, Layer* outLayer) { @@ -387,7 +406,17 @@ Error AidlComposer::getActiveConfig(Display display, Config* outConfig) { Error AidlComposer::getChangedCompositionTypes( Display display, std::vector<Layer>* outLayers, std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) { - const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display)); + std::vector<ChangedCompositionLayer> changedLayers; + Error error = Error::NONE; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + changedLayers = reader->get().takeChangedCompositionTypes(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } outLayers->reserve(changedLayers.size()); outTypes->reserve(changedLayers.size()); @@ -395,7 +424,7 @@ Error AidlComposer::getChangedCompositionTypes( outLayers->emplace_back(translate<Layer>(layer.layer)); outTypes->emplace_back(layer.composition); } - return Error::NONE; + return error; } Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) { @@ -447,7 +476,17 @@ Error AidlComposer::getDisplayName(Display display, std::string* outName) { Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers, std::vector<uint32_t>* outLayerRequestMasks) { - const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display)); + Error error = Error::NONE; + DisplayRequest displayRequests; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + displayRequests = reader->get().takeDisplayRequests(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask); outLayers->reserve(displayRequests.layerRequests.size()); outLayerRequestMasks->reserve(displayRequests.layerRequests.size()); @@ -456,7 +495,7 @@ Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequ outLayers->emplace_back(translate<Layer>(layer.layer)); outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask)); } - return Error::NONE; + return error; } Error AidlComposer::getDozeSupport(Display display, bool* outSupport) { @@ -496,16 +535,35 @@ Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp return static_cast<Error>(status.getServiceSpecificError()); } - *outTypes = translate<Hdr>(capabilities.types); + *outTypes = capabilities.types; *outMaxLuminance = capabilities.maxLuminance; *outMaxAverageLuminance = capabilities.maxAverageLuminance; *outMinLuminance = capabilities.minLuminance; return Error::NONE; } +Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) { + const auto status = mAidlComposerClient->getOverlaySupport(outProperties); + if (!status.isOk()) { + ALOGE("getOverlaySupport failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) { - auto fences = mReader.takeReleaseFences(translate<int64_t>(display)); + Error error = Error::NONE; + std::vector<ReleaseFences::Layer> fences; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + fences = reader->get().takeReleaseFences(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } outLayers->reserve(fences.size()); outReleaseFences->reserve(fences.size()); @@ -516,19 +574,29 @@ Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay *fence.fence.getR() = -1; outReleaseFences->emplace_back(fenceOwner); } - return Error::NONE; + return error; } Error AidlComposer::presentDisplay(Display display, int* outPresentFence) { ATRACE_NAME("HwcPresentDisplay"); - mWriter.presentDisplay(translate<int64_t>(display)); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().presentDisplay(translate<int64_t>(display)); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - auto fence = mReader.takePresentFence(translate<int64_t>(display)); + auto fence = reader->get().takePresentFence(translate<int64_t>(display)); + mMutex.unlock_shared(); // take ownership *outPresentFence = fence.get(); *fence.getR() = -1; @@ -553,11 +621,19 @@ Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<Gra handle = target->getNativeBuffer()->handle; } - mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence, - translate<aidl::android::hardware::graphics::common::Dataspace>( - dataspace), - translate<AidlRect>(damage)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get() + .setClientTarget(translate<int64_t>(display), slot, handle, acquireFence, + translate<aidl::android::hardware::graphics::common::Dataspace>( + dataspace), + translate<AidlRect>(damage)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) { @@ -573,14 +649,28 @@ Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent r } Error AidlComposer::setColorTransform(Display display, const float* matrix) { - mWriter.setColorTransform(translate<int64_t>(display), matrix); - return Error::NONE; + auto error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setColorTransform(translate<int64_t>(display), matrix); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer, int releaseFence) { - mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence)); - return Error::NONE; + auto error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) { @@ -618,16 +708,26 @@ Error AidlComposer::setClientTargetSlotCount(Display display) { Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes, uint32_t* outNumRequests) { ATRACE_NAME("HwcValidateDisplay"); - mWriter.validateDisplay(translate<int64_t>(display), - ClockMonotonicTimestamp{expectedPresentTime}); + const auto displayId = translate<int64_t>(display); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime}); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); + reader->get().hasChanges(displayId, outNumTypes, outNumRequests); + mMutex.unlock_shared(); return Error::NONE; } @@ -635,39 +735,59 @@ Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPr uint32_t* outNumTypes, uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) { ATRACE_NAME("HwcPresentOrValidateDisplay"); - mWriter.presentOrvalidateDisplay(translate<int64_t>(display), - ClockMonotonicTimestamp{expectedPresentTime}); + const auto displayId = translate<int64_t>(display); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().presentOrvalidateDisplay(displayId, + ClockMonotonicTimestamp{expectedPresentTime}); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display)); + const auto result = reader->get().takePresentOrValidateStage(displayId); if (!result.has_value()) { *state = translate<uint32_t>(-1); + mMutex.unlock_shared(); return Error::NO_RESOURCES; } *state = translate<uint32_t>(*result); if (*result == PresentOrValidate::Result::Presented) { - auto fence = mReader.takePresentFence(translate<int64_t>(display)); + auto fence = reader->get().takePresentFence(displayId); // take ownership *outPresentFence = fence.get(); *fence.getR() = -1; } if (*result == PresentOrValidate::Result::Validated) { - mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); + reader->get().hasChanges(displayId, outNumTypes, outNumRequests); } + mMutex.unlock_shared(); return Error::NONE; } Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) { - mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), + x, y); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot, @@ -677,90 +797,190 @@ Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot, handle = buffer->getNativeBuffer()->handle; } - mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle, - acquireFence); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, + handle, acquireFence); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer, const std::vector<IComposerClient::Rect>& damage) { - mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(damage)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(damage)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) { - mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer), - translate<BlendMode>(mode)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer), + translate<BlendMode>(mode)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) { - mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerCompositionType( Display display, Layer layer, aidl::android::hardware::graphics::composer3::Composition type) { - mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerCompositionType(translate<int64_t>(display), + translate<int64_t>(layer), type); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) { - mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlDataspace>(dataspace)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlDataspace>(dataspace)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer, const IComposerClient::Rect& frame) { - mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(frame)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(frame)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { - mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), + alpha); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSidebandStream(Display display, Layer layer, const native_handle_t* stream) { - mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), + stream); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSourceCrop(Display display, Layer layer, const IComposerClient::FRect& crop) { - mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlFRect>(crop)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlFRect>(crop)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) { - mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlTransform>(transform)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlTransform>(transform)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& visible) { - mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(visible)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(visible)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) { - mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } -Error AidlComposer::execute() { - const auto& commands = mWriter.getPendingCommands(); +Error AidlComposer::execute(Display display) { + auto writer = getWriter(display); + auto reader = getReader(display); + if (!writer || !reader) { + return Error::BAD_DISPLAY; + } + + const auto& commands = writer->get().getPendingCommands(); if (commands.empty()) { - mWriter.reset(); + writer->get().reset(); return Error::NONE; } @@ -772,9 +992,9 @@ Error AidlComposer::execute() { return static_cast<Error>(status.getServiceSpecificError()); } - mReader.parse(std::move(results)); + reader->get().parse(std::move(results)); } - const auto commandErrors = mReader.takeErrors(); + const auto commandErrors = reader->get().takeErrors(); Error error = Error::NONE; for (const auto& cmdErr : commandErrors) { const auto index = static_cast<size_t>(cmdErr.commandIndex); @@ -792,7 +1012,7 @@ Error AidlComposer::execute() { } } - mWriter.reset(); + writer->get().reset(); return error; } @@ -800,9 +1020,17 @@ Error AidlComposer::execute() { Error AidlComposer::setLayerPerFrameMetadata( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) { - mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlPerFrameMetadata>(perFrameMetadatas)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPerFrameMetadata(translate<int64_t>(display), + translate<int64_t>(layer), + translate<AidlPerFrameMetadata>(perFrameMetadatas)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys( @@ -862,8 +1090,16 @@ Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPo } Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) { - mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), + matrix); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat, @@ -926,20 +1162,36 @@ Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrame Error AidlComposer::setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) { - mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlPerFrameMetadataBlob>(metadata)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPerFrameMetadataBlobs(translate<int64_t>(display), + translate<int64_t>(layer), + translate<AidlPerFrameMetadataBlob>(metadata)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setDisplayBrightness(Display display, float brightness, float brightnessNits, const DisplayBrightnessOptions& options) { - mWriter.setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits); - - if (options.applyImmediately) { - return execute(); + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits); + + if (options.applyImmediately) { + error = execute(display); + mMutex.unlock_shared(); + return error; + } + } else { + error = Error::BAD_DISPLAY; } - - return Error::NONE; + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayCapabilities(Display display, @@ -1079,20 +1331,43 @@ Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* confi Error AidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { - *outClientTargetProperty = mReader.takeClientTargetProperty(translate<int64_t>(display)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + *outClientTargetProperty = + reader->get().takeClientTargetProperty(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) { - mWriter.setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), brightness); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), + brightness); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& blocking) { - mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(blocking)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(blocking)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayDecorationSupport(Display display, @@ -1130,5 +1405,88 @@ Error AidlComposer::getPhysicalDisplayOrientation(Display displayId, return Error::NONE; } +ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display) + REQUIRES_SHARED(mMutex) { + return mWriters.get(display); +} + +ftl::Optional<std::reference_wrapper<ComposerClientReader>> AidlComposer::getReader(Display display) + REQUIRES_SHARED(mMutex) { + if (mSingleReader) { + display = translate<Display>(kSingleReaderKey); + } + return mReaders.get(display); +} + +void AidlComposer::removeDisplay(Display display) { + mMutex.lock(); + bool wasErased = mWriters.erase(display); + ALOGW_IF(!wasErased, + "Attempting to remove writer for display %" PRId64 " which is not connected", + translate<int64_t>(display)); + if (!mSingleReader) { + removeReader(display); + } + mMutex.unlock(); +} + +void AidlComposer::onHotplugDisconnect(Display display) { + removeDisplay(display); +} + +bool AidlComposer::hasMultiThreadedPresentSupport(Display display) { + const auto displayId = translate<int64_t>(display); + std::vector<AidlDisplayCapability> capabilities; + const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities); + if (!status.isOk()) { + ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str()); + return false; + } + return std::find(capabilities.begin(), capabilities.end(), + AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end(); +} + +void AidlComposer::addReader(Display display) { + const auto displayId = translate<int64_t>(display); + std::optional<int64_t> displayOpt; + if (displayId != kSingleReaderKey) { + displayOpt.emplace(displayId); + } + auto [it, added] = mReaders.try_emplace(display, std::move(displayOpt)); + ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected", + displayId); +} + +void AidlComposer::removeReader(Display display) { + bool wasErased = mReaders.erase(display); + ALOGW_IF(!wasErased, + "Attempting to remove reader for display %" PRId64 " which is not connected", + translate<int64_t>(display)); +} + +void AidlComposer::addDisplay(Display display) { + const auto displayId = translate<int64_t>(display); + mMutex.lock(); + auto [it, added] = mWriters.try_emplace(display, displayId); + ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected", + displayId); + if (mSingleReader) { + if (hasMultiThreadedPresentSupport(display)) { + mSingleReader = false; + removeReader(translate<Display>(kSingleReaderKey)); + // Note that this includes the new display. + for (const auto& [existingDisplay, _] : mWriters) { + addReader(existingDisplay); + } + } + } else { + addReader(display); + } + mMutex.unlock(); +} + +void AidlComposer::onHotplugConnect(Display display) { + addDisplay(display); +} } // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 18d2242c7e..2a043fd0db 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -17,10 +17,12 @@ #pragma once #include "ComposerHal.h" +#include <ftl/shared_mutex.h> +#include <ftl/small_map.h> +#include <functional> #include <optional> #include <string> -#include <unordered_map> #include <utility> #include <vector> @@ -48,6 +50,7 @@ namespace android::Hwc2 { using aidl::android::hardware::graphics::common::DisplayDecorationSupport; using aidl::android::hardware::graphics::composer3::ComposerClientReader; using aidl::android::hardware::graphics::composer3::ComposerClientWriter; +using aidl::android::hardware::graphics::composer3::OverlayProperties; class AidlIComposerCallbackWrapper; @@ -69,10 +72,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - void resetCommands() override; + void resetCommands(Display) override; // Explicitly flush all pending commands in the command buffer. - Error executeCommands() override; + Error executeCommands(Display) override; uint32_t getMaxVirtualDisplayCount() override; Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, @@ -101,8 +104,9 @@ public: Error getDozeSupport(Display display, bool* outSupport) override; Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override; - Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, + Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) override; + Error getOverlaySupport(OverlayProperties* outProperties) override; Error getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) override; @@ -226,16 +230,29 @@ public: Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) override; + void onHotplugConnect(Display) override; + void onHotplugDisconnect(Display) override; private: // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call // this function to execute the command queue. - Error execute(); + Error execute(Display) REQUIRES_SHARED(mMutex); // returns the default instance name for the given service static std::string instance(const std::string& serviceName); + ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display) + REQUIRES_SHARED(mMutex); + ftl::Optional<std::reference_wrapper<ComposerClientReader>> getReader(Display) + REQUIRES_SHARED(mMutex); + void addDisplay(Display) EXCLUDES(mMutex); + void removeDisplay(Display) EXCLUDES(mMutex); + void addReader(Display) REQUIRES(mMutex); + void removeReader(Display) REQUIRES(mMutex); + + bool hasMultiThreadedPresentSupport(Display); + // 64KiB minus a small space for metadata such as read/write pointers static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16; // Max number of buffers that may be cached for a given layer @@ -243,8 +260,25 @@ private: // 1. Tightly coupling this cache to the max size of BufferQueue // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h) static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; - ComposerClientWriter mWriter; - ComposerClientReader mReader; + + // Without DisplayCapability::MULTI_THREADED_PRESENT, we use a single reader + // for all displays. With the capability, we use a separate reader for each + // display. + bool mSingleReader = true; + // Invalid displayId used as a key to mReaders when mSingleReader is true. + static constexpr int64_t kSingleReaderKey = 0; + + // TODO (b/256881188): Use display::PhysicalDisplayMap instead of hard-coded `3` + ftl::SmallMap<Display, ComposerClientWriter, 3> mWriters GUARDED_BY(mMutex); + ftl::SmallMap<Display, ComposerClientReader, 3> mReaders GUARDED_BY(mMutex); + // Protect access to mWriters and mReaders with a shared_mutex. Adding and + // removing a display require exclusive access, since the iterator or the + // writer/reader may be invalidated. Other calls need shared access while + // using the writer/reader, so they can use their display's writer/reader + // without it being deleted or the iterator being invalidated. + // TODO (b/257958323): Use std::shared_mutex and RAII once they support + // threading annotations. + ftl::SharedMutex mMutex; // Aidl interface using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index d266d942fb..ec23935dbd 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -38,6 +38,7 @@ #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> +#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> #include <aidl/android/hardware/graphics/common/Transform.h> #include <optional> @@ -65,7 +66,6 @@ using types::V1_0::Transform; using types::V1_1::RenderIntent; using types::V1_2::ColorMode; using types::V1_2::Dataspace; -using types::V1_2::Hdr; using types::V1_2::PixelFormat; using V2_1::Config; @@ -83,6 +83,7 @@ using PerFrameMetadata = IComposerClient::PerFrameMetadata; using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey; using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; using AidlTransform = ::aidl::android::hardware::graphics::common::Transform; +using aidl::android::hardware::graphics::common::Hdr; class Composer { public: @@ -109,10 +110,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - virtual void resetCommands() = 0; + virtual void resetCommands(Display) = 0; // Explicitly flush all pending commands in the command buffer. - virtual Error executeCommands() = 0; + virtual Error executeCommands(Display) = 0; virtual uint32_t getMaxVirtualDisplayCount() = 0; virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*, @@ -139,7 +140,7 @@ public: virtual Error getDozeSupport(Display display, bool* outSupport) = 0; virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0; - virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, + virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) = 0; @@ -281,6 +282,9 @@ public: virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0; virtual Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) = 0; + virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0; + virtual void onHotplugConnect(Display) = 0; + virtual void onHotplugDisconnect(Display) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index eb14933a61..ce602a8ad9 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -72,6 +72,10 @@ FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displa mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height); mConsumer->setMaxAcquiredBufferCount( SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1); + + for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) { + mHwcBufferIds[i] = UINT64_MAX; + } } void FramebufferSurface::resizeBuffers(const ui::Size& newSize) { @@ -88,31 +92,16 @@ status_t FramebufferSurface::prepareFrame(CompositionType /*compositionType*/) { } status_t FramebufferSurface::advanceFrame() { - uint32_t slot = 0; - sp<GraphicBuffer> buf; - sp<Fence> acquireFence(Fence::NO_FENCE); - Dataspace dataspace = Dataspace::UNKNOWN; - status_t result = nextBuffer(slot, buf, acquireFence, dataspace); - mDataSpace = dataspace; - if (result != NO_ERROR) { - ALOGE("error latching next FramebufferSurface buffer: %s (%d)", - strerror(-result), result); - } - return result; -} - -status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, - sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence, - Dataspace& outDataspace) { Mutex::Autolock lock(mMutex); BufferItem item; status_t err = acquireBufferLocked(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer); + mDataspace = Dataspace::UNKNOWN; return NO_ERROR; } else if (err != NO_ERROR) { ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err); + mDataspace = Dataspace::UNKNOWN; return err; } @@ -133,13 +122,18 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, mCurrentBufferSlot = item.mSlot; mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer; mCurrentFence = item.mFence; + mDataspace = static_cast<Dataspace>(item.mDataSpace); - outFence = item.mFence; - mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer); - outDataspace = static_cast<Dataspace>(item.mDataSpace); - status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace); + // assume HWC has previously seen the buffer in this slot + sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr); + if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) { + mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId(); + hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot + } + status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer, + mDataspace); if (result != NO_ERROR) { - ALOGE("error posting framebuffer: %d", result); + ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result); return result; } @@ -190,7 +184,7 @@ ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::S limitedSize.width = maxSize.height * aspectRatio; wasLimited = true; } - ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]", + ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]", limitedSize.width, limitedSize.height, size.width, size.height); return limitedSize; } @@ -198,9 +192,9 @@ ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::S void FramebufferSurface::dumpAsString(String8& result) const { Mutex::Autolock lock(mMutex); result.append(" FramebufferSurface\n"); - result.appendFormat(" mDataSpace=%s (%d)\n", - dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), - mDataSpace); + result.appendFormat(" mDataspace=%s (%d)\n", + dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(), + mDataspace); ConsumerBase::dumpLocked(result, " "); } diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index d41a856e68..0b863daf47 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -21,7 +21,7 @@ #include <sys/types.h> #include <compositionengine/DisplaySurface.h> -#include <compositionengine/impl/HwcBufferCache.h> +#include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> #include <ui/DisplayId.h> #include <ui/Size.h> @@ -69,12 +69,6 @@ private: virtual void dumpLocked(String8& result, const char* prefix) const; - // nextBuffer waits for and then latches the next buffer from the - // BufferQueue and releases the previously latched buffer to the - // BufferQueue. The new buffer is returned in the 'buffer' argument. - status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer, - sp<Fence>& outFence, ui::Dataspace& outDataspace); - const PhysicalDisplayId mDisplayId; // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of @@ -91,7 +85,7 @@ private: // compositing. Otherwise it will display the dataspace of the buffer // use for compositing which can change as wide-color content is // on/off. - ui::Dataspace mDataSpace; + ui::Dataspace mDataspace; // mCurrentBuffer is the current buffer or nullptr to indicate that there is // no current buffer. @@ -103,7 +97,9 @@ private: // Hardware composer, owned by SurfaceFlinger. HWComposer& mHwc; - compositionengine::impl::HwcBufferCache mHwcBufferCache; + // Buffers that HWC has seen before, indexed by slot number. + // NOTE: The BufferQueue slot number is the same as the HWC slot number. + uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS]; // Previous buffer to release after getting an updated retire fence bool mHasPendingRelease; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index c52e96d146..d0126d05ff 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -40,6 +40,7 @@ using aidl::android::hardware::graphics::composer3::Color; using aidl::android::hardware::graphics::composer3::Composition; using AidlCapability = aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -319,20 +320,25 @@ Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) const float maxLuminance = -1.0f; float maxAverageLuminance = -1.0f; float minLuminance = -1.0f; - std::vector<Hwc2::Hdr> types; - auto intError = mComposer.getHdrCapabilities(mId, &types, - &maxLuminance, &maxAverageLuminance, &minLuminance); + std::vector<Hwc2::Hdr> hdrTypes; + auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance, + &maxAverageLuminance, &minLuminance); auto error = static_cast<HWC2::Error>(intError); if (error != Error::NONE) { return error; } - *outCapabilities = HdrCapabilities(std::move(types), - maxLuminance, maxAverageLuminance, minLuminance); + *outCapabilities = + HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance); return Error::NONE; } +Error Display::getOverlaySupport(OverlayProperties* outProperties) const { + auto intError = mComposer.getOverlaySupport(outProperties); + return static_cast<Error>(intError); +} + Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat, Dataspace* outDataspace, uint8_t* outComponentMask) const { @@ -369,7 +375,7 @@ Error Display::getReleaseFences(std::unordered_map<HWC2::Layer*, sp<Fence>>* out for (uint32_t element = 0; element < numElements; ++element) { auto layer = getLayerById(layerIds[element]); if (layer) { - sp<Fence> fence(new Fence(fenceFds[element])); + sp<Fence> fence(sp<Fence>::make(fenceFds[element])); releaseFences.emplace(layer.get(), fence); } else { ALOGE("getReleaseFences: invalid layer %" PRIu64 @@ -394,7 +400,7 @@ Error Display::present(sp<Fence>* outPresentFence) return error; } - *outPresentFence = new Fence(presentFenceFd); + *outPresentFence = sp<Fence>::make(presentFenceFd); return Error::NONE; } @@ -532,7 +538,7 @@ Error Display::presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTy } if (*state == 1) { - *outPresentFence = new Fence(presentFenceFd); + *outPresentFence = sp<Fence>::make(presentFenceFd); } if (*state == 0) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 24aef9b73c..4971d19cc4 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -43,6 +43,7 @@ #include <aidl/android/hardware/graphics/composer3/Color.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> namespace android { @@ -70,7 +71,7 @@ namespace hal = android::hardware::graphics::composer::hal; struct ComposerCallback { virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0; virtual void onComposerHalRefresh(hal::HWDisplayId) = 0; - virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp, + virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp, std::optional<hal::VsyncPeriodNanos>) = 0; virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&) = 0; @@ -88,7 +89,7 @@ public: virtual hal::HWDisplayId getId() const = 0; virtual bool isConnected() const = 0; - virtual void setConnected(bool connected) = 0; // For use by Device only + virtual void setConnected(bool connected) = 0; // For use by HWComposer only virtual bool hasCapability( aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0; virtual bool isVsyncPeriodSwitchSupported() const = 0; @@ -117,6 +118,9 @@ public: [[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0; [[nodiscard]] virtual hal::Error getHdrCapabilities( android::HdrCapabilities* outCapabilities) const = 0; + [[nodiscard]] virtual hal::Error getOverlaySupport( + aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties) + const = 0; [[nodiscard]] virtual hal::Error getDisplayedContentSamplingAttributes( hal::PixelFormat* outFormat, hal::Dataspace* outDataspace, uint8_t* outComponentMask) const = 0; @@ -204,6 +208,8 @@ public: hal::Error getConnectionType(ui::DisplayConnectionType*) const override; hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex); hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override; + hal::Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties* + outProperties) const override; hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat, hal::Dataspace* outDataspace, uint8_t* outComponentMask) const override; @@ -253,7 +259,7 @@ public: // Other Display methods hal::HWDisplayId getId() const override { return mId; } bool isConnected() const override { return mIsConnected; } - void setConnected(bool connected) override; // For use by Device only + void setConnected(bool connected) override; bool hasCapability(aidl::android::hardware::graphics::composer3::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex); bool isVsyncPeriodSwitchSupported() const override; @@ -271,7 +277,7 @@ private: // Member variables - // These are references to data owned by HWC2::Device, which will outlive + // These are references to data owned by HWComposer, which will outlive // this HWC2::Display, so these references are guaranteed to be valid for // the lifetime of this object. android::Hwc2::Composer& mComposer; @@ -383,7 +389,7 @@ public: hal::Error setBlockingRegion(const android::Region& region) override; private: - // These are references to data owned by HWC2::Device, which will outlive + // These are references to data owned by HWComposer, which will outlive // this HWC2::Layer, so these references are guaranteed to be valid for // the lifetime of this object. android::Hwc2::Composer& mComposer; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index a6aee1f2f5..10fde2af8e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -96,6 +96,7 @@ HWComposer::~HWComposer() { void HWComposer::setCallback(HWC2::ComposerCallback& callback) { loadCapabilities(); loadLayerMetadataSupport(); + loadOverlayProperties(); if (mRegisteredCallback) { ALOGW("Callback already registered. Ignored extra registration attempt."); @@ -144,7 +145,7 @@ bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const { return mUpdateDeviceProductInfoOnHotplugReconnect; } -bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { +bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) { const auto displayId = toPhysicalDisplayId(hwcDisplayId); if (!displayId) { LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); @@ -160,13 +161,13 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { // with the same timestamp when turning the display off and on. This // is a bug in the HWC implementation, but filter the extra events // out here so they don't cause havoc downstream. - if (timestamp == displayData.lastHwVsync) { + if (timestamp == displayData.lastPresentTimestamp) { ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")", to_string(*displayId).c_str(), timestamp); return false; } - displayData.lastHwVsync = timestamp; + displayData.lastPresentTimestamp = timestamp; } const auto tag = "HW_VSYNC_" + to_string(*displayId); @@ -252,11 +253,7 @@ std::shared_ptr<HWC2::Layer> HWComposer::createLayer(HalDisplayId displayId) { } bool HWComposer::isConnected(PhysicalDisplayId displayId) const { - if (mDisplayData.count(displayId)) { - return mDisplayData.at(displayId).hwcDisplay->isConnected(); - } - - return false; + return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected(); } std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const { @@ -286,17 +283,12 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId d std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); - const auto hwcId = *fromPhysicalDisplayId(displayId); - ALOGV("[%" PRIu64 "] getActiveMode", hwcId); - hal::HWConfigId configId; - auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); - if (error == hal::Error::BAD_CONFIG) { - LOG_DISPLAY_ERROR(displayId, "No active mode"); - return std::nullopt; - } + hal::HWConfigId configId; + const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); + RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt); return configId; } @@ -494,6 +486,11 @@ sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const { return mDisplayData.at(displayId).lastPresentFence; } +nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, 0); + return mDisplayData.at(displayId).lastPresentTimestamp; +} + sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const { RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); const auto& displayFences = mDisplayData.at(displayId).releaseFences; @@ -517,7 +514,7 @@ status_t HWComposer::presentAndGetReleaseFences( if (displayData.validateWasSkipped) { // explicitly flush all pending commands - auto error = static_cast<hal::Error>(mComposer->executeCommands()); + auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId())); RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR); RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR); return NO_ERROR; @@ -656,6 +653,11 @@ status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* return NO_ERROR; } +const aidl::android::hardware::graphics::composer3::OverlayProperties& +HWComposer::getOverlaySupport() const { + return mOverlayProperties; +} + int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, 0); return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata(); @@ -932,6 +934,8 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( : "Secondary display", .deviceProductInfo = std::nullopt}; }(); + + mComposer->onHotplugConnect(hwcDisplayId); } if (!isConnected(info->id)) { @@ -951,18 +955,16 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( return {}; } - // The display will later be destroyed by a call to - // destroyDisplay(). For now we just mark it disconnected. - if (isConnected(*displayId)) { - mDisplayData[*displayId].hwcDisplay->setConnected(false); - } else { + if (!isConnected(*displayId)) { LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected"); + return {}; } - // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay - // via SurfaceFlinger's onHotplugReceived callback handling - return DisplayIdentificationInfo{.id = *displayId, - .name = std::string(), - .deviceProductInfo = std::nullopt}; + + // The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark + // it as disconnected. + mDisplayData.at(*displayId).hwcDisplay->setConnected(false); + mComposer->onHotplugDisconnect(hwcDisplayId); + return DisplayIdentificationInfo{.id = *displayId}; } void HWComposer::loadCapabilities() { @@ -973,6 +975,10 @@ void HWComposer::loadCapabilities() { } } +void HWComposer::loadOverlayProperties() { + mComposer->getOverlaySupport(&mOverlayProperties); +} + status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId, std::chrono::milliseconds timeout) { ATRACE_CALL(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 92a8f30f1b..78d4a68617 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -48,6 +48,7 @@ #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> namespace android { @@ -160,8 +161,9 @@ public: // reset state when a display is disconnected virtual void disconnectDisplay(HalDisplayId) = 0; - // get the present fence received from the last call to present. + // Get the present fence/timestamp received from the last call to present. virtual sp<Fence> getPresentFence(HalDisplayId) const = 0; + virtual nsecs_t getPresentTimestamp(PhysicalDisplayId) const = 0; // Get last release fence for the given layer virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0; @@ -177,6 +179,9 @@ public: // Fetches the HDR capabilities of the given display virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0; + virtual const aidl::android::hardware::graphics::composer3::OverlayProperties& + getOverlaySupport() const = 0; + virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0; // Returns the available RenderIntent of the given display. @@ -214,7 +219,7 @@ public: // TODO(b/157555476): Remove when the framework has proper support for headless mode virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0; - virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0; + virtual bool onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0; virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0; virtual bool isConnected(PhysicalDisplayId) const = 0; @@ -343,8 +348,9 @@ public: // reset state when a display is disconnected void disconnectDisplay(HalDisplayId) override; - // get the present fence received from the last call to present. + // Get the present fence/timestamp received from the last call to present. sp<Fence> getPresentFence(HalDisplayId) const override; + nsecs_t getPresentTimestamp(PhysicalDisplayId) const override; // Get last release fence for the given layer sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override; @@ -360,6 +366,9 @@ public: // Fetches the HDR capabilities of the given display status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override; + const aidl::android::hardware::graphics::composer3::OverlayProperties& getOverlaySupport() + const override; + int32_t getSupportedPerFrameMetadata(HalDisplayId) const override; // Returns the available RenderIntent of the given display. @@ -387,7 +396,7 @@ public: bool updatesDeviceProductInfoOnHotplugReconnect() const override; - bool onVsync(hal::HWDisplayId, int64_t timestamp) override; + bool onVsync(hal::HWDisplayId, nsecs_t timestamp) override; void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override; bool isConnected(PhysicalDisplayId) const override; @@ -456,7 +465,10 @@ private: struct DisplayData { std::unique_ptr<HWC2::Display> hwcDisplay; + sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires + nsecs_t lastPresentTimestamp = 0; + std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; bool validateWasSkipped; @@ -466,8 +478,6 @@ private: std::mutex vsyncEnabledLock; hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE; - - nsecs_t lastHwVsync = 0; }; std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId); @@ -479,11 +489,13 @@ private: void loadCapabilities(); void loadLayerMetadataSupport(); + void loadOverlayProperties(); std::unordered_map<HalDisplayId, DisplayData> mDisplayData; std::unique_ptr<android::Hwc2::Composer> mComposer; std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities; + aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties; std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata; bool mRegisteredCallback = false; diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index 473703423e..537d5450ad 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -20,6 +20,7 @@ #include <android/hardware/graphics/composer/2.4/IComposer.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <aidl/android/hardware/graphics/common/Hdr.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> @@ -39,7 +40,6 @@ using types::V1_0::Transform; using types::V1_1::RenderIntent; using types::V1_2::ColorMode; using types::V1_2::Dataspace; -using types::V1_2::Hdr; using types::V1_2::PixelFormat; using V2_1::Error; @@ -69,6 +69,7 @@ using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; using PowerMode = IComposerClient::PowerMode; using Vsync = IComposerClient::Vsync; using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints; +using Hdr = aidl::android::hardware::graphics::common::Hdr; } // namespace hardware::graphics::composer::hal @@ -177,6 +178,10 @@ inline std::string to_string(hardware::graphics::composer::hal::Error error) { return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error)); } +// For utils::Dumper ADL. +namespace hardware::graphics::composer { +namespace V2_2 { + inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) { switch (mode) { case hardware::graphics::composer::hal::PowerMode::OFF: @@ -194,6 +199,10 @@ inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) } } +} // namespace V2_2 + +namespace V2_1 { + inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) { switch (vsync) { case hardware::graphics::composer::hal::Vsync::ENABLE: @@ -205,4 +214,6 @@ inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) { } } +} // namespace V2_1 +} // namespace hardware::graphics::composer } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 2597ae6091..b607df0fd3 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -40,6 +40,7 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; using aidl::android::hardware::graphics::composer3::DimmingStage; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -272,11 +273,11 @@ void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) { } } -void HidlComposer::resetCommands() { +void HidlComposer::resetCommands(Display) { mWriter.reset(); } -Error HidlComposer::executeCommands() { +Error HidlComposer::executeCommands(Display) { return execute(); } @@ -495,13 +496,13 @@ Error HidlComposer::hasDisplayIdleTimerCapability(Display, bool*) { "OptionalFeature::KernelIdleTimer is not supported on HIDL"); } -Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, +Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { Error error = kDefaultError; if (mClient_2_3) { mClient_2_3->getHdrCapabilities_2_3(display, - [&](const auto& tmpError, const auto& tmpTypes, + [&](const auto& tmpError, const auto& tmpHdrTypes, const auto& tmpMaxLuminance, const auto& tmpMaxAverageLuminance, const auto& tmpMinLuminance) { @@ -509,15 +510,15 @@ Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp if (error != Error::NONE) { return; } + *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes); - *outTypes = tmpTypes; *outMaxLuminance = tmpMaxLuminance; *outMaxAverageLuminance = tmpMaxAverageLuminance; *outMinLuminance = tmpMinLuminance; }); } else { mClient->getHdrCapabilities(display, - [&](const auto& tmpError, const auto& tmpTypes, + [&](const auto& tmpError, const auto& tmpHdrTypes, const auto& tmpMaxLuminance, const auto& tmpMaxAverageLuminance, const auto& tmpMinLuminance) { @@ -525,11 +526,7 @@ Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp if (error != Error::NONE) { return; } - - outTypes->clear(); - for (auto type : tmpTypes) { - outTypes->push_back(static_cast<Hdr>(type)); - } + *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes); *outMaxLuminance = tmpMaxLuminance; *outMaxAverageLuminance = tmpMaxAverageLuminance; @@ -540,6 +537,10 @@ Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp return error; } +Error HidlComposer::getOverlaySupport(OverlayProperties* /*outProperties*/) { + return Error::NONE; +} + Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) { mReader.takeReleaseFences(display, outLayers, outReleaseFences); @@ -1352,6 +1353,9 @@ void HidlComposer::registerCallback(ComposerCallback& callback) { registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported)); } +void HidlComposer::onHotplugConnect(Display) {} +void HidlComposer::onHotplugDisconnect(Display) {} + CommandReader::~CommandReader() { resetData(); } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index d0d3c2e6d7..3602bbb24e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -56,7 +56,6 @@ using types::V1_0::Transform; using types::V1_1::RenderIntent; using types::V1_2::ColorMode; using types::V1_2::Dataspace; -using types::V1_2::Hdr; using types::V1_2::PixelFormat; using V2_1::Config; @@ -177,10 +176,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - void resetCommands() override; + void resetCommands(Display) override; // Explicitly flush all pending commands in the command buffer. - Error executeCommands() override; + Error executeCommands(Display) override; uint32_t getMaxVirtualDisplayCount() override; Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, @@ -209,8 +208,10 @@ public: Error getDozeSupport(Display display, bool* outSupport) override; Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override; - Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, + Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) override; + Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties* + outProperties) override; Error getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) override; @@ -337,6 +338,8 @@ public: Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) override; + void onHotplugConnect(Display) override; + void onHotplugDisconnect(Display) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index a0350b717a..f05223cce0 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -57,10 +57,9 @@ using android::hardware::power::Boost; using android::hardware::power::IPower; using android::hardware::power::IPowerHintSession; using android::hardware::power::Mode; +using android::hardware::power::SessionHint; using android::hardware::power::WorkDuration; -using scheduler::OneShotTimer; - PowerAdvisor::~PowerAdvisor() = default; namespace { @@ -142,7 +141,7 @@ void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expec } } -void PowerAdvisor::notifyDisplayUpdateImminent() { +void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { // Only start sending this notification once the system has booted so we don't introduce an // early-boot dependency on Power HAL if (!mBootFinished.load()) { @@ -156,7 +155,7 @@ void PowerAdvisor::notifyDisplayUpdateImminent() { return; } - if (!halWrapper->notifyDisplayUpdateImminent()) { + if (!halWrapper->notifyDisplayUpdateImminentAndCpuReset()) { // The HAL has become unavailable; attempt to reconnect later mReconnectPowerHal = true; return; @@ -196,7 +195,7 @@ bool PowerAdvisor::isPowerHintSessionRunning() { return mPowerHintSessionRunning; } -void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) { +void PowerAdvisor::setTargetWorkDuration(Duration targetDuration) { if (!usePowerHintSession()) { ALOGV("Power hint session target duration cannot be set, skipping"); return; @@ -215,13 +214,13 @@ void PowerAdvisor::sendActualWorkDuration() { ALOGV("Actual work duration power hint cannot be sent, skipping"); return; } - const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false); + const std::optional<Duration> actualDuration = estimateWorkDuration(false); if (actualDuration.has_value()) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(), - systemTime()); + halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin, + TimePoint::now()); } } } @@ -232,14 +231,14 @@ void PowerAdvisor::sendPredictedWorkDuration() { return; } - const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true); + const std::optional<Duration> predictedDuration = estimateWorkDuration(true); if (predictedDuration.has_value()) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(), - systemTime()); + halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin, + TimePoint::now()); } } } @@ -281,22 +280,22 @@ void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTim } } displayData.lastValidGpuStartTime = displayData.gpuStartTime; - displayData.lastValidGpuEndTime = signalTime; + displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime); } } displayData.gpuEndFenceTime = std::move(fenceTime); - displayData.gpuStartTime = systemTime(); + displayData.gpuStartTime = TimePoint::now(); } -void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, - nsecs_t validateEndTime) { +void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, + TimePoint validateEndTime) { DisplayTimingData& displayData = mDisplayTimingData[displayId]; displayData.hwcValidateStartTime = validateStartTime; displayData.hwcValidateEndTime = validateEndTime; } -void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, - nsecs_t presentEndTime) { +void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime, + TimePoint presentEndTime) { DisplayTimingData& displayData = mDisplayTimingData[displayId]; displayData.hwcPresentStartTime = presentStartTime; displayData.hwcPresentEndTime = presentEndTime; @@ -311,43 +310,41 @@ void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; } -void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) { +void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) { mExpectedPresentTimes.append(expectedPresentTime); } -void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) { +void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) { mLastSfPresentEndTime = presentEndTime; mLastPresentFenceTime = presentFenceTime; } -void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) { +void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) { mFrameDelayDuration = frameDelayDuration; } -void PowerAdvisor::setHwcPresentDelayedTime( - DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) { - mDisplayTimingData[displayId].hwcPresentDelayedTime = - (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime(); +void PowerAdvisor::setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) { + mDisplayTimingData[displayId].hwcPresentDelayedTime = earliestFrameStartTime; } -void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) { +void PowerAdvisor::setCommitStart(TimePoint commitStartTime) { mCommitStartTimes.append(commitStartTime); } -void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) { - mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime; +void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) { + mLastPostcompDuration = compositeEndTime - mLastSfPresentEndTime; } void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) { mDisplayIds = displayIds; } -void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) { +void PowerAdvisor::setTotalFrameTargetWorkDuration(Duration targetDuration) { mTotalFrameTargetDuration = targetDuration; } std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( - std::optional<nsecs_t> DisplayTimingData::*sortBy) { + std::optional<TimePoint> DisplayTimingData::*sortBy) { std::vector<DisplayId> sortedDisplays; std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays), [&](DisplayId id) { @@ -360,33 +357,34 @@ std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( return sortedDisplays; } -std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { +std::optional<Duration> PowerAdvisor::estimateWorkDuration(bool earlyHint) { if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) { return std::nullopt; } // Tracks when we finish presenting to hwc - nsecs_t estimatedEndTime = mCommitStartTimes[0]; + TimePoint estimatedEndTime = mCommitStartTimes[0]; // How long we spent this frame not doing anything, waiting for fences or vsync - nsecs_t idleDuration = 0; + Duration idleDuration = 0ns; // Most recent previous gpu end time in the current frame, probably from a prior display, used // as the start time for the next gpu operation if it ran over time since it probably blocked - std::optional<nsecs_t> previousValidGpuEndTime; + std::optional<TimePoint> previousValidGpuEndTime; // The currently estimated gpu end time for the frame, // used to accumulate gpu time as we iterate over the active displays - std::optional<nsecs_t> estimatedGpuEndTime; + std::optional<TimePoint> estimatedGpuEndTime; // If we're predicting at the start of the frame, we use last frame as our reference point // If we're predicting at the end of the frame, we use the current frame as a reference point - nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); + TimePoint referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); // When the prior frame should be presenting to the display // If we're predicting at the start of the frame, we use last frame's expected present time // If we're predicting at the end of the frame, the present fence time is already known - nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime); + TimePoint lastFramePresentTime = + (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime); // The timing info for the previously calculated display, if there was one std::optional<DisplayTimeline> previousDisplayReferenceTiming; @@ -427,10 +425,10 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { // Track how long we spent waiting for the fence, can be excluded from the timing estimate idleDuration += estimatedTiming.probablyWaitsForPresentFence ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime - : 0; + : 0ns; // Track how long we spent waiting to present, can be excluded from the timing estimate - idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration; + idleDuration += earlyHint ? 0ns : referenceTiming.hwcPresentDelayDuration; // Estimate the reference frame's gpu timing auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); @@ -438,15 +436,15 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; // Estimate the prediction frame's gpu end time from the reference frame - estimatedGpuEndTime = - std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) + + estimatedGpuEndTime = std::max(estimatedTiming.hwcPresentStartTime, + estimatedGpuEndTime.value_or(TimePoint{0ns})) + gpuTiming->duration; } previousDisplayReferenceTiming = referenceTiming; } - ATRACE_INT64("Idle duration", idleDuration); + ATRACE_INT64("Idle duration", idleDuration.ns()); - nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime; + TimePoint estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime; // Don't count time spent idly waiting in the estimate as we could do more work in that time estimatedEndTime -= idleDuration; @@ -454,21 +452,22 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { // We finish the frame when both present and the gpu are done, so wait for the later of the two // Also add the frame delay duration since the target did not move while we were delayed - nsecs_t totalDuration = mFrameDelayDuration + - std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0]; + Duration totalDuration = mFrameDelayDuration + + std::max(estimatedEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) - + mCommitStartTimes[0]; // We finish SurfaceFlinger when post-composition finishes, so add that in here - nsecs_t flingerDuration = + Duration flingerDuration = estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0]; // Combine the two timings into a single normalized one - nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); + Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); return std::make_optional(combinedDuration); } -nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) { - nsecs_t targetDuration; +Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) { + Duration targetDuration{0ns}; { std::lock_guard lock(mPowerHalMutex); targetDuration = *getPowerHal()->getTargetWorkDuration(); @@ -477,17 +476,18 @@ nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flin // Normalize total to the flinger target (vsync period) since that's how often we actually send // hints - nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration; + Duration normalizedTotalDuration = Duration::fromNs((targetDuration.ns() * totalDuration.ns()) / + mTotalFrameTargetDuration->ns()); return std::max(flingerDuration, normalizedTotalDuration); } PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference( - nsecs_t fenceTime, nsecs_t displayStartTime) { + TimePoint fenceTime, TimePoint displayStartTime) { DisplayTimeline estimated; estimated.hwcPresentStartTime = displayStartTime; // We don't predict waiting for vsync alignment yet - estimated.hwcPresentDelayDuration = 0; + estimated.hwcPresentDelayDuration = 0ns; // How long we expect to run before we start waiting for the fence // For now just re-use last frame's post-present duration and assume it will not change much @@ -502,12 +502,11 @@ PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFro } PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline( - nsecs_t fenceTime) { + TimePoint fenceTime) { DisplayTimeline timeline; // How long between calling hwc present and trying to wait on the fence - const nsecs_t fenceWaitStartDelay = - (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated) - .count(); + const Duration fenceWaitStartDelay = + (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated); // Did our reference frame wait for an appropriate vsync before calling into hwc const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() && @@ -522,7 +521,7 @@ PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayT // How long hwc present was delayed waiting for the next appropriate vsync timeline.hwcPresentDelayDuration = - (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0); + (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0ns); // When we started waiting for the present fence after calling into hwc present timeline.presentFenceWaitStartTime = timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay; @@ -537,23 +536,26 @@ PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayT } std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( - std::optional<nsecs_t> previousEnd) { + std::optional<TimePoint> previousEndTime) { if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { return std::nullopt; } - const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime); - const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime(); - nsecs_t gpuDuration = 0; - if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID && - latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) { + const TimePoint latestGpuStartTime = + std::max(previousEndTime.value_or(TimePoint{0ns}), *gpuStartTime); + const nsecs_t gpuEndFenceSignal = gpuEndFenceTime->getSignalTime(); + Duration gpuDuration{0ns}; + if (gpuEndFenceSignal != Fence::SIGNAL_TIME_INVALID && + gpuEndFenceSignal != Fence::SIGNAL_TIME_PENDING) { + const TimePoint latestGpuEndTime = TimePoint::fromNs(gpuEndFenceSignal); + // If we know how long the most recent gpu duration was, use that gpuDuration = latestGpuEndTime - latestGpuStartTime; } else if (lastValidGpuEndTime.has_value()) { // If we don't have the fence data, use the most recent information we do have gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime; - if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) { + if (gpuEndFenceSignal == Fence::SIGNAL_TIME_PENDING) { // If pending but went over the previous duration, use current time as the end - gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime); + gpuDuration = std::max(gpuDuration, Duration{TimePoint::now() - latestGpuStartTime}); } } return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime}; @@ -598,7 +600,7 @@ public: return ret.isOk(); } - bool notifyDisplayUpdateImminent() override { + bool notifyDisplayUpdateImminentAndCpuReset() override { // Power HAL 1.x doesn't have a notification for this ALOGV("HIDL notifyUpdateImminent received but can't send"); return true; @@ -614,15 +616,15 @@ public: bool startPowerHintSession() override { return false; } - void setTargetWorkDuration(int64_t) override {} + void setTargetWorkDuration(Duration) override {} - void sendActualWorkDuration(int64_t, nsecs_t) override {} + void sendActualWorkDuration(Duration, TimePoint) override {} bool shouldReconnectHAL() override { return false; } std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; } - std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; } + std::optional<Duration> getTargetWorkDuration() override { return std::nullopt; } private: const sp<V1_3::IPower> mPowerHal = nullptr; @@ -640,9 +642,6 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m } mSupportsPowerHint = checkPowerHintSessionSupported(); - - // Currently set to 0 to disable rate limiter by default - mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0); } AidlPowerHalWrapper::~AidlPowerHalWrapper() { @@ -677,8 +676,12 @@ bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) { return ret.isOk(); } -bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { - ALOGV("AIDL notifyDisplayUpdateImminent"); +bool AidlPowerHalWrapper::notifyDisplayUpdateImminentAndCpuReset() { + ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset"); + if (isPowerHintSessionRunning()) { + mPowerHintSession->sendHint(SessionHint::CPU_LOAD_RESET); + } + if (!mHasDisplayUpdateImminent) { ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); return true; @@ -730,9 +733,9 @@ bool AidlPowerHalWrapper::startPowerHintSession() { ALOGV("Cannot start power hint session, skipping"); return false; } - auto ret = - mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()), - mPowerHintThreadIds, mTargetDuration, &mPowerHintSession); + auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()), + mPowerHintThreadIds, mTargetDuration.ns(), + &mPowerHintSession); if (!ret.isOk()) { ALOGW("Failed to start power hint session with error: %s", ret.exceptionToString(ret.exceptionCode()).c_str()); @@ -742,14 +745,14 @@ bool AidlPowerHalWrapper::startPowerHintSession() { return isPowerHintSessionRunning(); } -void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { +void AidlPowerHalWrapper::setTargetWorkDuration(Duration targetDuration) { ATRACE_CALL(); mTargetDuration = targetDuration; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration); + if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns()); if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) { - ALOGV("Sending target time: %" PRId64 "ns", targetDuration); + ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns()); mLastTargetDurationSent = targetDuration; - auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration); + auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration.ns()); if (!ret.isOk()) { ALOGW("Failed to set power hint target work duration with error: %s", ret.exceptionMessage().c_str()); @@ -758,64 +761,40 @@ void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { } } -bool AidlPowerHalWrapper::shouldReportActualDurations() { - // Report if we have never reported before or are approaching a stale session - if (!mLastActualDurationSent.has_value() || - (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { - return true; - } - - if (!mActualDuration.has_value()) { - return false; - } - // Report if the change in actual duration exceeds the threshold - return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation; -} - -void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) { +void AidlPowerHalWrapper::sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) { ATRACE_CALL(); - - if (actualDuration < 0 || !isPowerHintSessionRunning()) { + if (actualDuration < 0ns || !isPowerHintSessionRunning()) { ALOGV("Failed to send actual work duration, skipping"); return; } - const nsecs_t reportedDuration = actualDuration; - - mActualDuration = reportedDuration; + mActualDuration = actualDuration; WorkDuration duration; - duration.durationNanos = reportedDuration; - duration.timeStampNanos = timestamp; + duration.durationNanos = actualDuration.ns(); + duration.timeStampNanos = timestamp.ns(); mPowerHintQueue.push_back(duration); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDuration); - ATRACE_INT64("Target error term", actualDuration - mTargetDuration); + ATRACE_INT64("Measured duration", actualDuration.ns()); + ATRACE_INT64("Target error term", Duration{actualDuration - mTargetDuration}.ns()); - ATRACE_INT64("Reported duration", reportedDuration); - ATRACE_INT64("Reported target", mLastTargetDurationSent); - ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent); + ATRACE_INT64("Reported duration", actualDuration.ns()); + ATRACE_INT64("Reported target", mLastTargetDurationSent.ns()); + ATRACE_INT64("Reported target error term", + Duration{actualDuration - mLastTargetDurationSent}.ns()); } ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 " with error: %" PRId64, - reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent); - - // This rate limiter queues similar duration reports to the powerhal into - // batches to avoid excessive binder calls. The criteria to send a given batch - // are outlined in shouldReportActualDurationsNow() - if (shouldReportActualDurations()) { - ALOGV("Sending hint update batch"); - mLastActualReportTimestamp = systemTime(); - auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); - if (!ret.isOk()) { - ALOGW("Failed to report actual work durations with error: %s", - ret.exceptionMessage().c_str()); - mShouldReconnectHal = true; - } - mPowerHintQueue.clear(); - // We save the actual duration here for rate limiting - mLastActualDurationSent = actualDuration; + actualDuration.ns(), mLastTargetDurationSent.ns(), + Duration{actualDuration - mLastTargetDurationSent}.ns()); + + auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); + if (!ret.isOk()) { + ALOGW("Failed to report actual work durations with error: %s", + ret.exceptionMessage().c_str()); + mShouldReconnectHal = true; } + mPowerHintQueue.clear(); } bool AidlPowerHalWrapper::shouldReconnectHAL() { @@ -826,14 +805,10 @@ std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() { return mPowerHintThreadIds; } -std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() { +std::optional<Duration> AidlPowerHalWrapper::getTargetWorkDuration() { return mTargetDuration; } -void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) { - mAllowedActualDeviation = allowedDeviation; -} - const bool AidlPowerHalWrapper::sTraceHintSessionData = base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); @@ -844,7 +819,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { // Grab old hint session values before we destroy any existing wrapper std::vector<int32_t> oldPowerHintSessionThreadIds; - std::optional<int64_t> oldTargetWorkDuration; + std::optional<Duration> oldTargetWorkDuration; if (mHalWrapper != nullptr) { oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds(); diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 6e25f787d7..d45e7cb572 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -27,6 +27,7 @@ #include <android/hardware/power/IPower.h> #include <compositionengine/impl/OutputCompositionState.h> +#include <scheduler/Time.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -47,13 +48,13 @@ public: virtual void onBootFinished() = 0; virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; virtual bool isUsingExpensiveRendering() = 0; - virtual void notifyDisplayUpdateImminent() = 0; + virtual void notifyDisplayUpdateImminentAndCpuReset() = 0; // Checks both if it supports and if it's enabled virtual bool usePowerHintSession() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; // Sends a power hint that updates to the target work duration for the frame - virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + virtual void setTargetWorkDuration(Duration targetDuration) = 0; // Sends a power hint for the actual known work duration at the end of the frame virtual void sendActualWorkDuration() = 0; // Sends a power hint for the upcoming frame predicted from previous frame timing @@ -65,33 +66,33 @@ public: // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0; // Reports the start and end times of a hwc validate call this frame for a given display - virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, - nsecs_t validateEndTime) = 0; + virtual void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, + TimePoint validateEndTime) = 0; // Reports the start and end times of a hwc present call this frame for a given display - virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, - nsecs_t presentEndTime) = 0; + virtual void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime, + TimePoint presentEndTime) = 0; // Reports the expected time that the current frame will present to the display - virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0; + virtual void setExpectedPresentTime(TimePoint expectedPresentTime) = 0; // Reports the most recent present fence time and end time once known - virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0; + virtual void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) = 0; // Reports whether a display used client composition this frame virtual void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) = 0; // Reports whether a given display skipped validation this frame virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0; // Reports when a hwc present is delayed, and the time that it will resume - virtual void setHwcPresentDelayedTime( - DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0; + virtual void setHwcPresentDelayedTime(DisplayId displayId, + TimePoint earliestFrameStartTime) = 0; // Reports the start delay for SurfaceFlinger this frame - virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0; + virtual void setFrameDelay(Duration frameDelayDuration) = 0; // Reports the SurfaceFlinger commit start time this frame - virtual void setCommitStart(nsecs_t commitStartTime) = 0; + virtual void setCommitStart(TimePoint commitStartTime) = 0; // Reports the SurfaceFlinger composite end time this frame - virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0; + virtual void setCompositeEnd(TimePoint compositeEndTime) = 0; // Reports the list of the currently active displays virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0; // Sets the target duration for the entire pipeline including the gpu - virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0; + virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0; }; namespace impl { @@ -105,17 +106,17 @@ public: virtual ~HalWrapper() = default; virtual bool setExpensiveRendering(bool enabled) = 0; - virtual bool notifyDisplayUpdateImminent() = 0; + virtual bool notifyDisplayUpdateImminentAndCpuReset() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; virtual void restartPowerHintSession() = 0; virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0; virtual bool startPowerHintSession() = 0; - virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; - virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0; + virtual void setTargetWorkDuration(Duration targetDuration) = 0; + virtual void sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) = 0; virtual bool shouldReconnectHAL() = 0; virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0; - virtual std::optional<nsecs_t> getTargetWorkDuration() = 0; + virtual std::optional<Duration> getTargetWorkDuration() = 0; }; PowerAdvisor(SurfaceFlinger& flinger); @@ -125,33 +126,31 @@ public: void onBootFinished() override; void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override; bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }; - void notifyDisplayUpdateImminent() override; + void notifyDisplayUpdateImminentAndCpuReset() override; bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; - void setTargetWorkDuration(nsecs_t targetDuration) override; + void setTargetWorkDuration(Duration targetDuration) override; void sendActualWorkDuration() override; void sendPredictedWorkDuration() override; void enablePowerHint(bool enabled) override; bool startPowerHintSession(const std::vector<int32_t>& threadIds) override; void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime); - void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime, - nsecs_t validateEndTime) override; - void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, - nsecs_t presentEndTime) override; + void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, + TimePoint validateEndTime) override; + void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime, + TimePoint presentEndTime) override; void setSkippedValidate(DisplayId displayId, bool skipped) override; void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override; - void setExpectedPresentTime(nsecs_t expectedPresentTime) override; - void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override; - void setHwcPresentDelayedTime( - DisplayId displayId, - std::chrono::steady_clock::time_point earliestFrameStartTime) override; - - void setFrameDelay(nsecs_t frameDelayDuration) override; - void setCommitStart(nsecs_t commitStartTime) override; - void setCompositeEnd(nsecs_t compositeEndTime) override; + void setExpectedPresentTime(TimePoint expectedPresentTime) override; + void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override; + void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override; + + void setFrameDelay(Duration frameDelayDuration) override; + void setCommitStart(TimePoint commitStartTime) override; + void setCompositeEnd(TimePoint compositeEndTime) override; void setDisplays(std::vector<DisplayId>& displayIds) override; - void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override; + void setTotalFrameTargetWorkDuration(Duration targetDuration) override; private: friend class PowerAdvisorTest; @@ -178,44 +177,45 @@ private: // Higher-level timing data used for estimation struct DisplayTimeline { // The start of hwc present, or the start of validate if it happened there instead - nsecs_t hwcPresentStartTime = -1; + TimePoint hwcPresentStartTime; // The end of hwc present or validate, whichever one actually presented - nsecs_t hwcPresentEndTime = -1; + TimePoint hwcPresentEndTime; // How long the actual hwc present was delayed after hwcPresentStartTime - nsecs_t hwcPresentDelayDuration = 0; + Duration hwcPresentDelayDuration{0ns}; // When we think we started waiting for the present fence after calling into hwc present and // after potentially waiting for the earliest present time - nsecs_t presentFenceWaitStartTime = -1; + TimePoint presentFenceWaitStartTime; // How long we ran after we finished waiting for the fence but before hwc present finished - nsecs_t postPresentFenceHwcPresentDuration = 0; + Duration postPresentFenceHwcPresentDuration{0ns}; // Are we likely to have waited for the present fence during composition bool probablyWaitsForPresentFence = false; // Estimate one frame's timeline from that of a previous frame - DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime); + DisplayTimeline estimateTimelineFromReference(TimePoint fenceTime, + TimePoint displayStartTime); }; struct GpuTimeline { - nsecs_t duration = 0; - nsecs_t startTime = -1; + Duration duration{0ns}; + TimePoint startTime; }; // Power hint session data recorded from the pipeline struct DisplayTimingData { std::unique_ptr<FenceTime> gpuEndFenceTime; - std::optional<nsecs_t> gpuStartTime; - std::optional<nsecs_t> lastValidGpuEndTime; - std::optional<nsecs_t> lastValidGpuStartTime; - std::optional<nsecs_t> hwcPresentStartTime; - std::optional<nsecs_t> hwcPresentEndTime; - std::optional<nsecs_t> hwcValidateStartTime; - std::optional<nsecs_t> hwcValidateEndTime; - std::optional<nsecs_t> hwcPresentDelayedTime; + std::optional<TimePoint> gpuStartTime; + std::optional<TimePoint> lastValidGpuEndTime; + std::optional<TimePoint> lastValidGpuStartTime; + std::optional<TimePoint> hwcPresentStartTime; + std::optional<TimePoint> hwcPresentEndTime; + std::optional<TimePoint> hwcValidateStartTime; + std::optional<TimePoint> hwcValidateEndTime; + std::optional<TimePoint> hwcPresentDelayedTime; bool usedClientComposition = false; bool skippedValidate = false; // Calculate high-level timing milestones from more granular display timing data - DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime); + DisplayTimeline calculateDisplayTimeline(TimePoint fenceTime); // Estimate the gpu duration for a given display from previous gpu timing data - std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd); + std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime); }; template <class T, size_t N> @@ -240,30 +240,31 @@ private: }; // Filter and sort the display ids by a given property - std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy); + std::vector<DisplayId> getOrderedDisplayIds( + std::optional<TimePoint> DisplayTimingData::*sortBy); // Estimates a frame's total work duration including gpu time. // Runs either at the beginning or end of a frame, using the most recent data available - std::optional<nsecs_t> estimateWorkDuration(bool earlyHint); + std::optional<Duration> estimateWorkDuration(bool earlyHint); // There are two different targets and actual work durations we care about, // this normalizes them together and takes the max of the two - nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration); + Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration); std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; // Current frame's delay - nsecs_t mFrameDelayDuration = 0; + Duration mFrameDelayDuration{0ns}; // Last frame's post-composition duration - nsecs_t mLastPostcompDuration = 0; + Duration mLastPostcompDuration{0ns}; // Buffer of recent commit start times - RingBuffer<nsecs_t, 2> mCommitStartTimes; + RingBuffer<TimePoint, 2> mCommitStartTimes; // Buffer of recent expected present times - RingBuffer<nsecs_t, 2> mExpectedPresentTimes; - // Most recent present fence time, set at the end of the frame once known - nsecs_t mLastPresentFenceTime = -1; - // Most recent present fence time, set at the end of the frame once known - nsecs_t mLastSfPresentEndTime = -1; - // Target for the entire pipeline including gpu - std::optional<nsecs_t> mTotalFrameTargetDuration; + RingBuffer<TimePoint, 2> mExpectedPresentTimes; + // Most recent present fence time, provided by SF after composition engine finishes presenting + TimePoint mLastPresentFenceTime; + // Most recent composition engine present end time, returned with the present fence from SF + TimePoint mLastSfPresentEndTime; + // Target duration for the entire pipeline including gpu + std::optional<Duration> mTotalFrameTargetDuration; // Updated list of display IDs std::vector<DisplayId> mDisplayIds; @@ -273,11 +274,11 @@ private: // An adjustable safety margin which pads the "actual" value sent to PowerHAL, // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error - static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms; + static constexpr const Duration kTargetSafetyMargin{1ms}; // How long we expect hwc to run after the present call until it waits for the fence - static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us; - static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us; + static constexpr const Duration kFenceWaitStartDelayValidated{150us}; + static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us}; }; class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { @@ -288,27 +289,23 @@ public: static std::unique_ptr<HalWrapper> connect(); bool setExpensiveRendering(bool enabled) override; - bool notifyDisplayUpdateImminent() override; + bool notifyDisplayUpdateImminentAndCpuReset() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; void restartPowerHintSession() override; void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override; bool startPowerHintSession() override; - void setTargetWorkDuration(nsecs_t targetDuration) override; - void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override; + void setTargetWorkDuration(Duration targetDuration) override; + void sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) override; bool shouldReconnectHAL() override; std::vector<int32_t> getPowerHintSessionThreadIds() override; - std::optional<nsecs_t> getTargetWorkDuration() override; + std::optional<Duration> getTargetWorkDuration() override; private: friend class AidlPowerHalWrapperTest; bool checkPowerHintSessionSupported(); void closePowerHintSession(); - bool shouldReportActualDurations(); - - // Used for testing - void setAllowedActualDeviation(nsecs_t); const sp<hardware::power::IPower> mPowerHal = nullptr; bool mHasExpensiveRendering = false; @@ -323,24 +320,15 @@ private: // Queue of actual durations saved to report std::vector<hardware::power::WorkDuration> mPowerHintQueue; // The latest values we have received for target and actual - nsecs_t mTargetDuration = kDefaultTarget.count(); - std::optional<nsecs_t> mActualDuration; + Duration mTargetDuration = kDefaultTargetDuration; + std::optional<Duration> mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector<int32_t> mPowerHintThreadIds; bool mSupportsPowerHint = false; - // Keep track of the last messages sent for rate limiter change detection - std::optional<nsecs_t> mLastActualDurationSent; - // Timestamp of the last report we sent, used to avoid stale sessions - nsecs_t mLastActualReportTimestamp = 0; - nsecs_t mLastTargetDurationSent = kDefaultTarget.count(); - // Max amount the error term can vary without causing an actual value report - nsecs_t mAllowedActualDeviation = -1; + Duration mLastTargetDurationSent = kDefaultTargetDuration; // Whether we should emit ATRACE_INT data for hint sessions static const bool sTraceHintSessionData; - static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms; - // Amount of time after the last message was sent before the session goes stale - // actually 100ms but we use 80 here to ideally avoid going stale - static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; + static constexpr Duration kDefaultTargetDuration{16ms}; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 3803a78670..d62075ec65 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -103,6 +103,10 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId d sink->setAsyncMode(true); IGraphicBufferProducer::QueueBufferOutput output; mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output); + + for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) { + mHwcBufferIds[i] = UINT64_MAX; + } } VirtualDisplaySurface::~VirtualDisplaySurface() { @@ -197,9 +201,9 @@ status_t VirtualDisplaySurface::advanceFrame() { return NO_MEMORY; } - sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ? - mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr); - sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; + sp<GraphicBuffer> const& fbBuffer = + mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr); + sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot]; VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); @@ -211,12 +215,14 @@ status_t VirtualDisplaySurface::advanceFrame() { status_t result = NO_ERROR; if (fbBuffer != nullptr) { - uint32_t hwcSlot = 0; - sp<GraphicBuffer> hwcBuffer; - mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer); - + // assume that HWC has previously seen the buffer in this slot + sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr); + if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) { + mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId(); + hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot + } // TODO: Correctly propagate the dataspace from GL composition - result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer, + result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer, ui::Dataspace::UNKNOWN); } diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index e21095aa88..be06e2bb10 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -20,7 +20,7 @@ #include <string> #include <compositionengine/DisplaySurface.h> -#include <compositionengine/impl/HwcBufferCache.h> +#include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> #include <gui/IGraphicBufferProducer.h> #include <ui/DisplayId.h> @@ -164,6 +164,10 @@ private: sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_* uint32_t mDefaultOutputFormat; + // Buffers that HWC has seen before, indexed by HWC slot number. + // NOTE: The BufferQueue slot number is the same as the HWC slot number. + uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS]; + // // Inter-frame state // @@ -260,8 +264,6 @@ private: bool mMustRecompose = false; - compositionengine::impl::HwcBufferCache mHwcBufferCache; - bool mForceHwcCopy; }; diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp deleted file mode 100644 index e8c590e3c4..0000000000 --- a/services/surfaceflinger/EffectLayer.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -// #define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "EffectLayer" - -#include "EffectLayer.h" - -#include <stdint.h> -#include <stdlib.h> -#include <sys/types.h> - -#include <compositionengine/CompositionEngine.h> -#include <compositionengine/LayerFECompositionState.h> -#include <renderengine/RenderEngine.h> -#include <ui/GraphicBuffer.h> -#include <utils/Errors.h> -#include <utils/Log.h> - -#include "DisplayDevice.h" -#include "SurfaceFlinger.h" - -namespace android { -// --------------------------------------------------------------------------- - -EffectLayer::EffectLayer(const LayerCreationArgs& args) - : Layer(args), - mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {} - -EffectLayer::~EffectLayer() = default; - -std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClientCompositionList( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { - std::vector<compositionengine::LayerFE::LayerSettings> results; - std::optional<compositionengine::LayerFE::LayerSettings> layerSettings = - prepareClientComposition(targetSettings); - // Nothing to render. - if (!layerSettings) { - return {}; - } - - // set the shadow for the layer if needed - prepareShadowClientComposition(*layerSettings, targetSettings.viewport); - - // If fill bounds are occluded or the fill color is invalid skip the fill settings. - if (targetSettings.realContentIsVisible && fillsColor()) { - // Set color for color fill settings. - layerSettings->source.solidColor = getColor().rgb; - results.push_back(*layerSettings); - } else if (hasBlur() || drawShadows()) { - layerSettings->skipContentDraw = true; - results.push_back(*layerSettings); - } - - return results; -} - -bool EffectLayer::isVisible() const { - return !isHiddenByPolicy() && (getAlpha() > 0.0_hf || hasBlur()) && hasSomethingToDraw(); -} - -bool EffectLayer::setColor(const half3& color) { - if (mDrawingState.color.r == color.r && mDrawingState.color.g == color.g && - mDrawingState.color.b == color.b) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.color.r = color.r; - mDrawingState.color.g = color.g; - mDrawingState.color.b = color.b; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool EffectLayer::setDataspace(ui::Dataspace dataspace) { - if (mDrawingState.dataspace == dataspace) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.dataspace = dataspace; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -void EffectLayer::preparePerFrameCompositionState() { - Layer::preparePerFrameCompositionState(); - - auto* compositionState = editCompositionState(); - compositionState->color = getColor(); - compositionState->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; -} - -sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const { - // There's no need to get a CE Layer if the EffectLayer isn't going to draw anything. In that - // case, it acts more like a ContainerLayer so returning a null CE Layer makes more sense - if (hasSomethingToDraw()) { - return asLayerFE(); - } else { - return nullptr; - } -} - -compositionengine::LayerFECompositionState* EffectLayer::editCompositionState() { - return mCompositionState.get(); -} - -const compositionengine::LayerFECompositionState* EffectLayer::getCompositionState() const { - return mCompositionState.get(); -} - -bool EffectLayer::isOpaque(const Layer::State& s) const { - // Consider the layer to be opaque if its opaque flag is set or its effective - // alpha (considering the alpha of its parents as well) is 1.0; - return (s.flags & layer_state_t::eLayerOpaque) != 0 || (fillsColor() && getAlpha() == 1.0_hf); -} - -ui::Dataspace EffectLayer::getDataSpace() const { - return mDrawingState.dataspace; -} - -sp<Layer> EffectLayer::createClone() { - sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer( - LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata())); - layer->setInitialValuesForClone(this); - return layer; -} - -bool EffectLayer::fillsColor() const { - return mDrawingState.color.r >= 0.0_hf && mDrawingState.color.g >= 0.0_hf && - mDrawingState.color.b >= 0.0_hf; -} - -bool EffectLayer::hasBlur() const { - return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0; -} - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h deleted file mode 100644 index 1dcb633251..0000000000 --- a/services/surfaceflinger/EffectLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2007 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 <sys/types.h> - -#include <cstdint> - -#include "Layer.h" - -namespace android { - -// A layer that can render a combination of the following effects. -// * fill the bounds of the layer with a color -// * render a shadow cast by the bounds of the layer -// If no effects are enabled, the layer is considered to be invisible. -class EffectLayer : public Layer { -public: - explicit EffectLayer(const LayerCreationArgs&); - ~EffectLayer() override; - - sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override; - compositionengine::LayerFECompositionState* editCompositionState() override; - - const char* getType() const override { return "EffectLayer"; } - bool isVisible() const override; - - bool setColor(const half3& color) override; - - bool setDataspace(ui::Dataspace dataspace) override; - - ui::Dataspace getDataSpace() const override; - - bool isOpaque(const Layer::State& s) const override; - -protected: - /* - * compositionengine::LayerFE overrides - */ - const compositionengine::LayerFECompositionState* getCompositionState() const override; - void preparePerFrameCompositionState() override; - std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) override; - - std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState; - - sp<Layer> createClone() override; - -private: - // Returns true if there is a valid color to fill. - bool fillsColor() const; - // Returns true if this layer has a blur value. - bool hasBlur() const; - bool hasSomethingToDraw() const { return fillsColor() || drawShadows() || hasBlur(); } -}; - -} // namespace android diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp index e12835f0f6..155cf4da58 100644 --- a/services/surfaceflinger/FpsReporter.cpp +++ b/services/surfaceflinger/FpsReporter.cpp @@ -56,14 +56,15 @@ void FpsReporter::dispatchLayerFps() { mFlinger.mCurrentState.traverse([&](Layer* layer) { auto& currentState = layer->getDrawingState(); - if (currentState.metadata.has(METADATA_TASK_ID)) { - int32_t taskId = currentState.metadata.getInt32(METADATA_TASK_ID, 0); + if (currentState.metadata.has(gui::METADATA_TASK_ID)) { + int32_t taskId = currentState.metadata.getInt32(gui::METADATA_TASK_ID, 0); if (seenTasks.count(taskId) == 0) { // localListeners is expected to be tiny for (TrackedListener& listener : localListeners) { if (listener.taskId == taskId) { seenTasks.insert(taskId); - listenersAndLayersToReport.push_back({listener, sp<Layer>(layer)}); + listenersAndLayersToReport.push_back( + {listener, sp<Layer>::fromExisting(layer)}); break; } } @@ -90,7 +91,7 @@ void FpsReporter::binderDied(const wp<IBinder>& who) { void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, int32_t taskId) { sp<IBinder> asBinder = IInterface::asBinder(listener); - asBinder->linkToDeath(this); + asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); std::lock_guard lock(mMutex); mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, taskId}); } diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 66beff2953..cd1ba70d84 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -109,11 +109,11 @@ std::string jankTypeBitmaskToString(int32_t jankType) { jankType &= ~JankType::DisplayHAL; } if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) { - janks.emplace_back("SurfaceFlinger CPU Deadline Missed"); + janks.emplace_back("SurfaceFlinger deadline missed (while in HWC)"); jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed; } if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) { - janks.emplace_back("SurfaceFlinger GPU Deadline Missed"); + janks.emplace_back("SurfaceFlinger deadline missed (while in GPU comp)"); jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed; } if (jankType & JankType::AppDeadlineMissed) { @@ -289,7 +289,7 @@ nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, minTime = std::min(minTime, actuals.endTime); } if (actuals.presentTime != 0) { - minTime = std::min(minTime, actuals.endTime); + minTime = std::min(minTime, actuals.presentTime); } return minTime; } @@ -892,6 +892,10 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& mJankType = JankType::Unknown; deadlineDelta = 0; deltaToVsync = 0; + if (mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { + mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime; + } + return; } @@ -986,11 +990,8 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& mJankClassificationThresholds.presentThreshold) { // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame // was presented more than a vsync late. - if (mGpuFence != FenceTime::NO_FENCE && - mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime < - mRefreshRate.getPeriodNsecs()) { - // If SF was in GPU composition and the CPU work finished before the vsync - // period, classify it as GPU deadline missed. + if (mGpuFence != FenceTime::NO_FENCE) { + // If SF was in GPU composition, classify it as GPU deadline missed. mJankType = JankType::SurfaceFlingerGpuDeadlineMissed; } else { mJankType = JankType::SurfaceFlingerCpuDeadlineMissed; @@ -1171,22 +1172,50 @@ float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { static_cast<float>(totalPresentToPresentWalls); } +std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const { + for (size_t i = 0; i < mPendingPresentFences.size(); i++) { + const auto& [fence, _] = mPendingPresentFences[i]; + if (fence && fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + return i; + } + } + + return {}; +} + void FrameTimeline::flushPendingPresentFences() { + const auto firstSignaledFence = getFirstSignalFenceIndex(); + if (!firstSignaledFence.has_value()) { + return; + } + // Perfetto is using boottime clock to void drifts when the device goes // to suspend. const auto monoBootOffset = mUseBootTimeClock ? (systemTime(SYSTEM_TIME_BOOTTIME) - systemTime(SYSTEM_TIME_MONOTONIC)) : 0; + // Present fences are expected to be signaled in order. Mark all the previous + // pending fences as errors. + for (size_t i = 0; i < firstSignaledFence.value(); i++) { + const auto& pendingPresentFence = *mPendingPresentFences.begin(); + const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; + auto& displayFrame = pendingPresentFence.second; + displayFrame->onPresent(signalTime, mPreviousPresentTime); + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset); + mPendingPresentFences.erase(mPendingPresentFences.begin()); + } + for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; if (pendingPresentFence.first && pendingPresentFence.first->isValid()) { signalTime = pendingPresentFence.first->getSignalTime(); if (signalTime == Fence::SIGNAL_TIME_PENDING) { - continue; + break; } } + auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousPresentTime); displayFrame->trace(mSurfaceFlingerPid, monoBootOffset); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index a2305af554..31074b1959 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -474,6 +474,7 @@ private: friend class android::frametimeline::FrameTimelineTest; void flushPendingPresentFences() REQUIRES(mMutex); + std::optional<size_t> getFirstSignalFenceIndex() const REQUIRES(mMutex); void finalizeCurrentDisplayFrame() REQUIRES(mMutex); void dumpAll(std::string& result); void dumpJank(std::string& result); diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h new file mode 100644 index 0000000000..0c7b24a2c4 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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 <gui/DisplayInfo.h> + +namespace android::surfaceflinger::frontend { + +// Display information needed to populate input and calculate layer geometry. +struct DisplayInfo { + gui::DisplayInfo info; + ui::Transform transform; + bool receivesInput; + bool isSecure; + // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed. + bool isPrimary; + ui::Transform::RotationFlags rotationFlags; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp new file mode 100644 index 0000000000..6d492c0f4a --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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 "LayerCreationArgs.h" +#include <binder/IPCThreadState.h> +#include <private/android_filesystem_config.h> +#include "Client.h" +#include "gui/LayerMetadata.h" + +namespace android::surfaceflinger { + +std::atomic<uint32_t> LayerCreationArgs::sSequence{1}; + +LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name, + uint32_t flags, gui::LayerMetadata metadataArg, + std::optional<uint32_t> id) + : flinger(flinger), + client(std::move(client)), + name(std::move(name)), + flags(flags), + metadata(std::move(metadataArg)) { + IPCThreadState* ipc = IPCThreadState::self(); + ownerPid = ipc->getCallingPid(); + uid_t callingUid = ipc->getCallingUid(); + metadata.setInt32(gui::METADATA_CALLING_UID, static_cast<int32_t>(callingUid)); + ownerUid = callingUid; + if (ownerUid == AID_GRAPHICS || ownerUid == AID_SYSTEM) { + // System can override the calling UID/PID since it can create layers on behalf of apps. + ownerPid = metadata.getInt32(gui::METADATA_OWNER_PID, ownerPid); + ownerUid = static_cast<uid_t>( + metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid))); + } + + if (id) { + sequence = *id; + sSequence = *id + 1; + } else { + sequence = sSequence++; + if (sequence == UNASSIGNED_LAYER_ID) { + ALOGW("Layer sequence id rolled over."); + sequence = sSequence++; + } + } +} + +LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args) + : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {} + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h new file mode 100644 index 0000000000..7b5a157871 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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/Binder.h> +#include <gui/LayerMetadata.h> +#include <utils/StrongPointer.h> +#include <cstdint> +#include <limits> +#include <optional> +constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max(); + +namespace android { +class SurfaceFlinger; +class Client; +} // namespace android + +namespace android::surfaceflinger { + +struct LayerCreationArgs { + static std::atomic<uint32_t> sSequence; + + LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name, + uint32_t flags, gui::LayerMetadata, + std::optional<uint32_t> id = std::nullopt); + LayerCreationArgs(const LayerCreationArgs&); + + android::SurfaceFlinger* flinger; + sp<android::Client> client; + std::string name; + uint32_t flags; // ISurfaceComposerClient flags + gui::LayerMetadata metadata; + pid_t ownerPid; + uid_t ownerUid; + uint32_t textureName; + uint32_t sequence; + bool addToRoot = true; + wp<IBinder> parentHandle = nullptr; + wp<IBinder> mirrorLayerHandle = nullptr; +}; + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp new file mode 100644 index 0000000000..75e4e3ae11 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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 "LayerHandle.h" +#include <cstdint> +#include "Layer.h" +#include "LayerCreationArgs.h" +#include "SurfaceFlinger.h" + +namespace android::surfaceflinger { + +LayerHandle::LayerHandle(const sp<android::SurfaceFlinger>& flinger, + const sp<android::Layer>& layer) + : mFlinger(flinger), mLayer(layer), mLayerId(static_cast<uint32_t>(layer->getSequence())) {} + +LayerHandle::~LayerHandle() { + if (mFlinger) { + mFlinger->onHandleDestroyed(this, mLayer, mLayerId); + } +} + +const String16 LayerHandle::kDescriptor = String16("android.Layer.LayerHandle"); + +sp<LayerHandle> LayerHandle::fromIBinder(const sp<IBinder>& binder) { + if (binder == nullptr) { + return nullptr; + } + + BBinder* b = binder->localBinder(); + if (b == nullptr || b->getInterfaceDescriptor() != LayerHandle::kDescriptor) { + ALOGD("handle does not have a valid descriptor"); + return nullptr; + } + + // We can safely cast this binder since its local and we verified its interface descriptor. + return sp<LayerHandle>::cast(binder); +} + +sp<android::Layer> LayerHandle::getLayer(const sp<IBinder>& binder) { + sp<LayerHandle> handle = LayerHandle::fromIBinder(binder); + return handle ? handle->mLayer : nullptr; +} + +uint32_t LayerHandle::getLayerId(const sp<IBinder>& binder) { + sp<LayerHandle> handle = LayerHandle::fromIBinder(binder); + return handle ? handle->mLayerId : UNASSIGNED_LAYER_ID; +} + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.h b/services/surfaceflinger/FrontEnd/LayerHandle.h new file mode 100644 index 0000000000..5d0f783515 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerHandle.h @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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/Binder.h> +#include <utils/StrongPointer.h> + +namespace android { +class SurfaceFlinger; +class Layer; +} // namespace android + +namespace android::surfaceflinger { + +/* + * The layer handle is just a BBinder object passed to the client + * (remote process) -- we don't keep any reference on our side such that + * the dtor is called when the remote side let go of its reference. + * + * ~LayerHandle ensures that mFlinger->onLayerDestroyed() is called for + * this layer when the handle is destroyed. + */ +class LayerHandle : public BBinder { +public: + LayerHandle(const sp<android::SurfaceFlinger>& flinger, const sp<android::Layer>& layer); + // for testing + LayerHandle(uint32_t layerId) : mFlinger(nullptr), mLayer(nullptr), mLayerId(layerId) {} + ~LayerHandle(); + + // Static functions to access the layer and layer id safely from an incoming binder. + static sp<LayerHandle> fromIBinder(const sp<IBinder>& handle); + static sp<android::Layer> getLayer(const sp<IBinder>& handle); + static uint32_t getLayerId(const sp<IBinder>& handle); + static const String16 kDescriptor; + + const String16& getInterfaceDescriptor() const override { return kDescriptor; } + +private: + sp<android::SurfaceFlinger> mFlinger; + sp<android::Layer> mLayer; + const uint32_t mLayerId; +}; + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp new file mode 100644 index 0000000000..db4e8af484 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -0,0 +1,472 @@ +/* + * Copyright 2022 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 +#undef LOG_TAG +#define LOG_TAG "LayerHierarchy" + +#include "LayerHierarchy.h" +#include "SwapErase.h" + +namespace android::surfaceflinger::frontend { + +namespace { +auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs, + const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) { + auto lhsLayer = lhs.first->getLayer(); + auto rhsLayer = rhs.first->getLayer(); + if (lhsLayer->layerStack != rhsLayer->layerStack) { + return lhsLayer->layerStack.id < rhsLayer->layerStack.id; + } + if (lhsLayer->z != rhsLayer->z) { + return lhsLayer->z < rhsLayer->z; + } + return lhsLayer->id < rhsLayer->id; +}; + +void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec, + std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) { + auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare); + vec.insert(it, std::move(value)); +} +} // namespace + +LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {} + +LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) { + mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer; + mChildren = hierarchy.mChildren; +} + +void LayerHierarchy::traverse(const Visitor& visitor, + LayerHierarchy::TraversalPath& traversalPath) const { + if (mLayer) { + bool breakTraversal = !visitor(*this, traversalPath); + if (breakTraversal) { + return; + } + } + if (traversalPath.hasRelZLoop()) { + LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId); + } + for (auto& [child, childVariant] : mChildren) { + ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id, + childVariant); + child->traverse(visitor, traversalPath); + } +} + +void LayerHierarchy::traverseInZOrder(const Visitor& visitor, + LayerHierarchy::TraversalPath& traversalPath) const { + bool traverseThisLayer = (mLayer != nullptr); + for (auto it = mChildren.begin(); it < mChildren.end(); it++) { + auto& [child, childVariant] = *it; + if (traverseThisLayer && child->getLayer()->z >= 0) { + bool breakTraversal = !visitor(*this, traversalPath); + if (breakTraversal) { + return; + } + traverseThisLayer = false; + } + if (childVariant == LayerHierarchy::Variant::Detached) { + continue; + } + ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id, + childVariant); + child->traverseInZOrder(visitor, traversalPath); + } + + if (traverseThisLayer) { + visitor(*this, traversalPath); + } +} + +void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) { + insertSorted(mChildren, {child, variant}); +} + +void LayerHierarchy::removeChild(LayerHierarchy* child) { + auto it = std::find_if(mChildren.begin(), mChildren.end(), + [child](const std::pair<LayerHierarchy*, Variant>& x) { + return x.first == child; + }); + if (it == mChildren.end()) { + LOG_ALWAYS_FATAL("Could not find child!"); + } + mChildren.erase(it); +} + +void LayerHierarchy::sortChildrenByZOrder() { + std::sort(mChildren.begin(), mChildren.end(), layerZCompare); +} + +void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) { + auto it = std::find_if(mChildren.begin(), mChildren.end(), + [hierarchy](std::pair<LayerHierarchy*, Variant>& child) { + return child.first == hierarchy; + }); + if (it == mChildren.end()) { + LOG_ALWAYS_FATAL("Could not find child!"); + } else { + it->second = variant; + } +} + +const RequestedLayerState* LayerHierarchy::getLayer() const { + return mLayer; +} + +std::string LayerHierarchy::getDebugStringShort() const { + std::string debug = "LayerHierarchy{"; + debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " "; + if (mChildren.empty()) { + debug += "no children"; + } else { + debug += std::to_string(mChildren.size()) + " children"; + } + return debug + "}"; +} + +std::string LayerHierarchy::getDebugString(const char* prefix) const { + std::string debug = prefix + getDebugStringShort(); + for (auto& [child, childVariant] : mChildren) { + std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant); + debug += "\n" + child->getDebugString(childPrefix.c_str()); + } + return debug; +} + +bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const { + outInvalidRelativeRoot = UNASSIGNED_LAYER_ID; + traverse([&outInvalidRelativeRoot](const LayerHierarchy&, + const LayerHierarchy::TraversalPath& traversalPath) -> bool { + if (traversalPath.hasRelZLoop()) { + outInvalidRelativeRoot = traversalPath.invalidRelativeRootId; + return false; + } + return true; + }); + return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID; +} + +LayerHierarchyBuilder::LayerHierarchyBuilder( + const std::vector<std::unique_ptr<RequestedLayerState>>& layers) { + mHierarchies.reserve(layers.size()); + mLayerIdToHierarchy.reserve(layers.size()); + for (auto& layer : layers) { + mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get())); + mLayerIdToHierarchy[layer->id] = mHierarchies.back().get(); + } + for (const auto& layer : layers) { + onLayerAdded(layer.get()); + } + detachHierarchyFromRelativeParent(&mOffscreenRoot); +} + +void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) { + auto layer = hierarchy->mLayer; + LayerHierarchy::Variant type = layer->hasValidRelativeParent() + ? LayerHierarchy::Variant::Detached + : LayerHierarchy::Variant::Attached; + + LayerHierarchy* parent; + + if (layer->parentId != UNASSIGNED_LAYER_ID) { + parent = getHierarchyFromId(layer->parentId); + } else if (layer->canBeRoot) { + parent = &mRoot; + } else { + parent = &mOffscreenRoot; + } + parent->addChild(hierarchy, type); + hierarchy->mParent = parent; +} + +void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) { + hierarchy->mParent->removeChild(hierarchy); + hierarchy->mParent = nullptr; +} + +void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) { + auto layer = hierarchy->mLayer; + if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) { + return; + } + + if (layer->relativeParentId != UNASSIGNED_LAYER_ID) { + hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId); + } else { + hierarchy->mRelativeParent = &mOffscreenRoot; + } + hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative); + hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached); +} + +void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) { + if (hierarchy->mRelativeParent) { + hierarchy->mRelativeParent->removeChild(hierarchy); + } + hierarchy->mRelativeParent = nullptr; + hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached); +} + +void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) { + if (root->mLayer) { + attachToRelativeParent(root); + } + for (auto& [child, childVariant] : root->mChildren) { + if (childVariant == LayerHierarchy::Variant::Detached || + childVariant == LayerHierarchy::Variant::Attached) { + attachHierarchyToRelativeParent(child); + } + } +} + +void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) { + if (root->mLayer) { + detachFromRelativeParent(root); + } + for (auto& [child, childVariant] : root->mChildren) { + if (childVariant == LayerHierarchy::Variant::Detached || + childVariant == LayerHierarchy::Variant::Attached) { + detachHierarchyFromRelativeParent(child); + } + } +} + +void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) { + LayerHierarchy* hierarchy = getHierarchyFromId(layer->id); + attachToParent(hierarchy); + attachToRelativeParent(hierarchy); + + if (layer->mirrorId != UNASSIGNED_LAYER_ID) { + LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId); + hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror); + } +} + +void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) { + LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false); + if (!hierarchy) { + // Layer was never part of the hierarchy if it was created and destroyed in the same + // transaction. + return; + } + // detach from parent + detachFromRelativeParent(hierarchy); + detachFromParent(hierarchy); + + // detach children + for (auto& [child, variant] : hierarchy->mChildren) { + if (variant == LayerHierarchy::Variant::Attached || + variant == LayerHierarchy::Variant::Detached) { + mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached); + child->mParent = &mOffscreenRoot; + } else if (variant == LayerHierarchy::Variant::Relative) { + mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached); + child->mRelativeParent = &mOffscreenRoot; + } + } + + swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) { + return layerHierarchy.get() == hierarchy; + }); + mLayerIdToHierarchy.erase(layer->id); +} + +void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) { + LayerHierarchy* hierarchy = getHierarchyFromId(layer->id); + auto it = hierarchy->mChildren.begin(); + while (it != hierarchy->mChildren.end()) { + if (it->second == LayerHierarchy::Variant::Mirror) { + hierarchy->mChildren.erase(it); + break; + } + it++; + } + + if (layer->mirrorId != UNASSIGNED_LAYER_ID) { + hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror); + } +} + +void LayerHierarchyBuilder::update( + const std::vector<std::unique_ptr<RequestedLayerState>>& layers, + const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) { + // rebuild map + for (auto& layer : layers) { + if (layer->changes.test(RequestedLayerState::Changes::Created)) { + mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get())); + mLayerIdToHierarchy[layer->id] = mHierarchies.back().get(); + } + } + + for (auto& layer : layers) { + if (layer->changes.get() == 0) { + continue; + } + if (layer->changes.test(RequestedLayerState::Changes::Created)) { + onLayerAdded(layer.get()); + continue; + } + LayerHierarchy* hierarchy = getHierarchyFromId(layer->id); + if (layer->changes.test(RequestedLayerState::Changes::Parent)) { + detachFromParent(hierarchy); + attachToParent(hierarchy); + } + if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) { + detachFromRelativeParent(hierarchy); + attachToRelativeParent(hierarchy); + } + if (layer->changes.test(RequestedLayerState::Changes::Z)) { + hierarchy->mParent->sortChildrenByZOrder(); + if (hierarchy->mRelativeParent) { + hierarchy->mRelativeParent->sortChildrenByZOrder(); + } + } + if (layer->changes.test(RequestedLayerState::Changes::Mirror)) { + updateMirrorLayer(layer.get()); + } + } + + for (auto& layer : destroyedLayers) { + onLayerDestroyed(layer.get()); + } + // When moving from onscreen to offscreen and vice versa, we need to attach and detach + // from our relative parents. This walks down both trees to do so. We can optimize this + // further by tracking onscreen, offscreen state in LayerHierarchy. + detachHierarchyFromRelativeParent(&mOffscreenRoot); + attachHierarchyToRelativeParent(&mRoot); +} + +const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const { + return mRoot; +} + +const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const { + return mOffscreenRoot; +} + +std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const { + if (depth > 10) return "too deep, loop?"; + if (layerId == UNASSIGNED_LAYER_ID) return ""; + auto it = mLayerIdToHierarchy.find(layerId); + if (it == mLayerIdToHierarchy.end()) return "not found"; + + LayerHierarchy* hierarchy = it->second; + if (!hierarchy->mLayer) return "none"; + + std::string debug = + "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name; + if (hierarchy->mRelativeParent) { + debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort(); + } + if (hierarchy->mParent) { + debug += " Parent:" + hierarchy->mParent->getDebugStringShort(); + } + return debug; +} + +LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId, + bool childrenOnly) const { + auto it = mLayerIdToHierarchy.find(layerId); + if (it == mLayerIdToHierarchy.end()) return {nullptr}; + + LayerHierarchy hierarchy(*it->second, childrenOnly); + return hierarchy; +} + +LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) { + auto it = mLayerIdToHierarchy.find(layerId); + if (it == mLayerIdToHierarchy.end()) { + if (crashOnFailure) { + LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId); + } + return nullptr; + }; + + return it->second; +} + +LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID = + {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached}; + +std::string LayerHierarchy::TraversalPath::toString() const { + std::string debugString = "TraversalPath{.id = " + std::to_string(id); + + if (!mirrorRootIds.empty()) { + debugString += ", .mirrorRootIds="; + for (auto rootId : mirrorRootIds) { + debugString += std::to_string(rootId) + ","; + } + } + + if (!relativeRootIds.empty()) { + debugString += ", .relativeRootIds="; + for (auto rootId : relativeRootIds) { + debugString += std::to_string(rootId) + ","; + } + } + + if (hasRelZLoop()) { + debugString += ", hasRelZLoop=true invalidRelativeRootId="; + debugString += std::to_string(invalidRelativeRootId) + ","; + } + + debugString += "}"; + return debugString; +} + +// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out +// of scope the TraversalPath is reset to its original state. +LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath, + uint32_t layerId, + LayerHierarchy::Variant variant) + : mTraversalPath(traversalPath), + mParentId(traversalPath.id), + mParentVariant(traversalPath.variant) { + // Update the traversal id with the child layer id and variant. Parent id and variant are + // stored to reset the id upon destruction. + traversalPath.id = layerId; + traversalPath.variant = variant; + if (variant == LayerHierarchy::Variant::Mirror) { + traversalPath.mirrorRootIds.emplace_back(layerId); + } + if (variant == LayerHierarchy::Variant::Relative) { + if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(), + layerId) != traversalPath.relativeRootIds.end()) { + traversalPath.invalidRelativeRootId = layerId; + } + traversalPath.relativeRootIds.emplace_back(layerId); + } +} +LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() { + // Reset the traversal id to its original parent state using the state that was saved in + // the constructor. + if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) { + mTraversalPath.mirrorRootIds.pop_back(); + } + if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) { + mTraversalPath.relativeRootIds.pop_back(); + } + if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) { + mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID; + } + mTraversalPath.id = mParentId; + mTraversalPath.variant = mParentVariant; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h new file mode 100644 index 0000000000..f83a859de7 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -0,0 +1,163 @@ +/* + * Copyright 2022 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 "FrontEnd/LayerCreationArgs.h" +#include "RequestedLayerState.h" +#include "ftl/small_vector.h" + +namespace android::surfaceflinger::frontend { +class LayerHierarchyBuilder; + +// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal. +// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not +// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph. +// +// Each node in the hierarchy can be visited by multiple parents (making this a graph). While +// traversing the hierarchy, a new concept called Variant can be used to understand the +// relationship of the layer to its parent. The following variants are possible: +// Attached - child of the parent +// Detached - child of the parent but currently relative parented to another layer +// Relative - relative child of the parent +// Mirror - mirrored from another layer +// +// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without +// cloning the layer requested state. The mirrored hierarchy and its corresponding +// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any +// states. +class LayerHierarchy { +public: + enum Variant { + Attached, + Detached, + Relative, + Mirror, + }; + // Represents a unique path to a node. + struct TraversalPath { + uint32_t id; + LayerHierarchy::Variant variant; + // Mirrored layers can have a different geometry than their parents so we need to track + // the mirror roots in the traversal. + ftl::SmallVector<uint32_t, 5> mirrorRootIds; + // Relative layers can be visited twice, once by their parent and then once again by + // their relative parent. We keep track of the roots here to detect any loops in the + // hierarchy. If a relative root already exists in the list while building the + // TraversalPath, it means that somewhere in the hierarchy two layers are relatively + // parented to each other. + ftl::SmallVector<uint32_t, 5> relativeRootIds; + // First duplicate relative root id found. If this is a valid layer id that means we are + // in a loop. + uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID; + bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; } + bool isRelative() { return !relativeRootIds.empty(); } + + bool operator==(const TraversalPath& other) const { + return id == other.id && mirrorRootIds == other.mirrorRootIds; + } + std::string toString() const; + + static TraversalPath ROOT_TRAVERSAL_ID; + }; + + // Helper class to add nodes to an existing traversal id and removes the + // node when it goes out of scope. + class ScopedAddToTraversalPath { + public: + ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId, + LayerHierarchy::Variant variantArg); + ~ScopedAddToTraversalPath(); + + private: + TraversalPath& mTraversalPath; + uint32_t mParentId; + LayerHierarchy::Variant mParentVariant; + }; + LayerHierarchy(RequestedLayerState* layer); + + // Visitor function that provides the hierarchy node and a traversal id which uniquely + // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState. + // Return false to stop traversing down the hierarchy. + typedef std::function<bool(const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath& traversalPath)> + Visitor; + + // Traverse the hierarchy and visit all child variants. + void traverse(const Visitor& visitor) const { + traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID); + } + + // Traverse the hierarchy in z-order, skipping children that have relative parents. + void traverseInZOrder(const Visitor& visitor) const { + traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID); + } + + const RequestedLayerState* getLayer() const; + std::string getDebugString(const char* prefix = "") const; + std::string getDebugStringShort() const; + // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot + // will contain the first relative root that was visited twice in a traversal. + bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const; + std::vector<std::pair<LayerHierarchy*, Variant>> mChildren; + +private: + friend LayerHierarchyBuilder; + LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly); + void addChild(LayerHierarchy*, LayerHierarchy::Variant); + void removeChild(LayerHierarchy*); + void sortChildrenByZOrder(); + void updateChild(LayerHierarchy*, LayerHierarchy::Variant); + void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const; + void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const; + + const RequestedLayerState* mLayer; + LayerHierarchy* mParent = nullptr; + LayerHierarchy* mRelativeParent = nullptr; +}; + +// Given a list of RequestedLayerState, this class will build a root hierarchy and an +// offscreen hierarchy. The builder also has an update method which can update an existing +// hierarchy from a list of RequestedLayerState and associated change flags. +class LayerHierarchyBuilder { +public: + LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&); + void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers, + const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers); + LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const; + const LayerHierarchy& getHierarchy() const; + const LayerHierarchy& getOffscreenHierarchy() const; + std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const; + +private: + void onLayerAdded(RequestedLayerState* layer); + void attachToParent(LayerHierarchy*); + void detachFromParent(LayerHierarchy*); + void attachToRelativeParent(LayerHierarchy*); + void detachFromRelativeParent(LayerHierarchy*); + void attachHierarchyToRelativeParent(LayerHierarchy*); + void detachHierarchyFromRelativeParent(LayerHierarchy*); + + void onLayerDestroyed(RequestedLayerState* layer); + void updateMirrorLayer(RequestedLayerState* layer); + LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true); + std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy; + std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies; + LayerHierarchy mRoot{nullptr}; + LayerHierarchy mOffscreenRoot{nullptr}; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp new file mode 100644 index 0000000000..fdf60b3dfc --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -0,0 +1,335 @@ +/* + * Copyright 2022 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 + +#undef LOG_TAG +#define LOG_TAG "LayerLifecycleManager" + +#include "LayerLifecycleManager.h" +#include "Layer.h" // temporarily needed for LayerHandle +#include "LayerHandle.h" +#include "SwapErase.h" + +namespace android::surfaceflinger::frontend { + +using namespace ftl::flag_operators; + +void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) { + if (newLayers.empty()) { + return; + } + + mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; + for (auto& newLayer : newLayers) { + RequestedLayerState& layer = *newLayer.get(); + auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer}); + if (!inserted) { + LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id, + it->second.owner.getDebugString().c_str()); + } + + layer.parentId = linkLayer(layer.parentId, layer.id); + layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id); + layer.mirrorId = linkLayer(layer.mirrorId, layer.id); + layer.touchCropId = linkLayer(layer.touchCropId, layer.id); + + mLayers.emplace_back(std::move(newLayer)); + } +} + +void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) { + std::vector<uint32_t> layersToBeDestroyed; + for (const auto& layerId : destroyedHandles) { + auto it = mIdToLayer.find(layerId); + if (it == mIdToLayer.end()) { + LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId); + continue; + } + RequestedLayerState& layer = it->second.owner; + layer.handleAlive = false; + if (!layer.canBeDestroyed()) { + continue; + } + layer.changes |= RequestedLayerState::Changes::Destroyed; + layersToBeDestroyed.emplace_back(layerId); + } + + if (layersToBeDestroyed.empty()) { + return; + } + + mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; + for (size_t i = 0; i < layersToBeDestroyed.size(); i++) { + uint32_t layerId = layersToBeDestroyed[i]; + auto it = mIdToLayer.find(layerId); + if (it == mIdToLayer.end()) { + LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId); + continue; + } + + RequestedLayerState& layer = it->second.owner; + + layer.parentId = unlinkLayer(layer.parentId, layer.id); + layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id); + layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id); + layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id); + + auto& references = it->second.references; + for (uint32_t linkedLayerId : references) { + RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId); + if (!linkedLayer) { + LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__, + linkedLayerId, layer.id); + continue; + }; + if (linkedLayer->parentId == layer.id) { + linkedLayer->parentId = UNASSIGNED_LAYER_ID; + if (linkedLayer->canBeDestroyed()) { + linkedLayer->changes |= RequestedLayerState::Changes::Destroyed; + layersToBeDestroyed.emplace_back(linkedLayer->id); + } + } + if (linkedLayer->relativeParentId == layer.id) { + linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID; + } + if (linkedLayer->mirrorId == layer.id) { + linkedLayer->mirrorId = UNASSIGNED_LAYER_ID; + } + if (linkedLayer->touchCropId == layer.id) { + linkedLayer->touchCropId = UNASSIGNED_LAYER_ID; + } + } + mIdToLayer.erase(it); + } + + auto it = mLayers.begin(); + while (it != mLayers.end()) { + RequestedLayerState* layer = it->get(); + if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) { + ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str()); + std::iter_swap(it, mLayers.end() - 1); + mDestroyedLayers.emplace_back(std::move(mLayers.back())); + if (it == mLayers.end() - 1) { + it = mLayers.erase(mLayers.end() - 1); + } else { + mLayers.erase(mLayers.end() - 1); + } + } else { + it++; + } + } +} + +void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions) { + for (const auto& transaction : transactions) { + for (const auto& resolvedComposerState : transaction.states) { + const auto& clientState = resolvedComposerState.state; + uint32_t layerId = LayerHandle::getLayerId(clientState.surface); + if (layerId == UNASSIGNED_LAYER_ID) { + ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get()); + continue; + } + + RequestedLayerState* layer = getLayerFromId(layerId); + if (layer == nullptr) { + LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__, + clientState.surface.get(), layerId); + continue; + } + + if (!layer->handleAlive) { + LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of " + "order LayerLifecycleManager updates", + __func__, clientState.surface.get(), layerId); + continue; + } + + uint32_t oldParentId = layer->parentId; + uint32_t oldRelativeParentId = layer->relativeParentId; + uint32_t oldTouchCropId = layer->touchCropId; + layer->merge(resolvedComposerState); + + if (layer->what & layer_state_t::eBackgroundColorChanged) { + if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) { + LayerCreationArgs backgroundLayerArgs{nullptr, + nullptr, + layer->name + "BackgroundColorLayer", + ISurfaceComposerClient::eFXSurfaceEffect, + {}}; + std::vector<std::unique_ptr<RequestedLayerState>> newLayers; + newLayers.emplace_back( + std::make_unique<RequestedLayerState>(backgroundLayerArgs)); + RequestedLayerState* backgroundLayer = newLayers.back().get(); + backgroundLayer->handleAlive = false; + backgroundLayer->parentId = layer->id; + backgroundLayer->z = std::numeric_limits<int32_t>::min(); + backgroundLayer->color.rgb = layer->color.rgb; + backgroundLayer->color.a = layer->bgColorAlpha; + backgroundLayer->dataspace = layer->bgColorDataspace; + + layer->bgColorLayerId = backgroundLayer->id; + addLayers({std::move(newLayers)}); + } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && + layer->bgColorAlpha == 0) { + RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); + bgColorLayer->parentId = UNASSIGNED_LAYER_ID; + onHandlesDestroyed({layer->bgColorLayerId}); + } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) { + RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); + bgColorLayer->color.rgb = layer->color.rgb; + bgColorLayer->color.a = layer->bgColorAlpha; + bgColorLayer->dataspace = layer->bgColorDataspace; + mGlobalChanges |= RequestedLayerState::Changes::Content; + } + } + + if (oldParentId != layer->parentId) { + unlinkLayer(oldParentId, layer->id); + layer->parentId = linkLayer(layer->parentId, layer->id); + } + if (oldRelativeParentId != layer->relativeParentId) { + unlinkLayer(oldRelativeParentId, layer->id); + layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id); + } + if (oldTouchCropId != layer->touchCropId) { + unlinkLayer(oldTouchCropId, layer->id); + layer->touchCropId = linkLayer(layer->touchCropId, layer->id); + } + + mGlobalChanges |= layer->changes & + (RequestedLayerState::Changes::Hierarchy | + RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::Content); + } + } +} + +void LayerLifecycleManager::commitChanges() { + for (auto& layer : mLayers) { + if (layer->changes.test(RequestedLayerState::Changes::Created)) { + for (auto listener : mListeners) { + listener->onLayerAdded(*layer); + } + } + layer->what = 0; + layer->changes.clear(); + } + + for (auto& destroyedLayer : mDestroyedLayers) { + if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) { + for (auto listener : mListeners) { + listener->onLayerAdded(*destroyedLayer); + } + } + + for (auto listener : mListeners) { + listener->onLayerDestroyed(*destroyedLayer); + } + } + mDestroyedLayers.clear(); + mGlobalChanges.clear(); +} + +void LayerLifecycleManager::addLifecycleListener(std::shared_ptr<ILifecycleListener> listener) { + mListeners.emplace_back(std::move(listener)); +} + +void LayerLifecycleManager::removeLifecycleListener(std::shared_ptr<ILifecycleListener> listener) { + swapErase(mListeners, listener); +} + +const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getLayers() const { + return mLayers; +} + +const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getDestroyedLayers() + const { + return mDestroyedLayers; +} + +const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const { + return mGlobalChanges; +} + +RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) { + if (id == UNASSIGNED_LAYER_ID) { + return nullptr; + } + auto it = mIdToLayer.find(id); + if (it == mIdToLayer.end()) { + return nullptr; + } + return &it->second.owner; +} + +std::vector<uint32_t>* LayerLifecycleManager::getLinkedLayersFromId(uint32_t id) { + if (id == UNASSIGNED_LAYER_ID) { + return nullptr; + } + auto it = mIdToLayer.find(id); + if (it == mIdToLayer.end()) { + return nullptr; + } + return &it->second.references; +} + +uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) { + if (layerId == UNASSIGNED_LAYER_ID) { + return UNASSIGNED_LAYER_ID; + } + + std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId); + if (!linkedLayers) { + ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId, + layerToLink); + return UNASSIGNED_LAYER_ID; + } + linkedLayers->emplace_back(layerToLink); + return layerId; +} + +uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) { + std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId); + if (!linkedLayers) { + return UNASSIGNED_LAYER_ID; + } + swapErase(*linkedLayers, linkedLayer); + return UNASSIGNED_LAYER_ID; +} + +std::string LayerLifecycleManager::References::getDebugString() const { + std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:"; + std::for_each(references.begin(), references.end(), + [&debugInfo = debugInfo](const uint32_t& reference) mutable { + debugInfo += std::to_string(reference) + ","; + }); + return debugInfo; +} + +void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) { + auto it = mIdToLayer.find(relativeRootId); + if (it == mIdToLayer.end()) { + return; + } + RequestedLayerState& layer = it->second.owner; + layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id); + layer.changes |= + RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent; + mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h new file mode 100644 index 0000000000..63a7afca4e --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h @@ -0,0 +1,101 @@ +/* + * Copyright 2022 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 "RequestedLayerState.h" +#include "TransactionState.h" + +namespace android::surfaceflinger::frontend { + +// Owns a collection of RequestedLayerStates and manages their lifecycle +// and state changes. +// +// RequestedLayerStates are tracked and destroyed if they have no parent and +// no handle left to keep them alive. The handle does not keep a reference to +// the RequestedLayerState but a layer id associated with the RequestedLayerState. +// If the handle is destroyed and the RequestedLayerState does not have a parent, +// the LayerLifecycleManager destroys the RequestedLayerState. +// +// Threading: This class is not thread safe, it requires external synchronization. +// +// Typical usage: Input states (new layers, transactions, destroyed layer handles) +// are collected in the background passed into the LayerLifecycleManager to update +// layer lifecycle and layer state at start of composition. +class LayerLifecycleManager { +public: + // External state changes should be updated in the following order: + void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>); + void applyTransactions(const std::vector<TransactionState>&); + void onHandlesDestroyed(const std::vector<uint32_t>&); + + // Detaches the layer from its relative parent to prevent a loop in the + // layer hierarchy. This overrides the RequestedLayerState and leaves + // the system in an invalid state. This is always a client error that + // needs to be fixed but overriding the state allows us to fail gracefully. + void fixRelativeZLoop(uint32_t relativeRootId); + + // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all + // ILifecycleListener callbacks and clears any change flags from previous state + // updates. This function should be called outside the hot path since it's not + // critical to composition. + void commitChanges(); + + class ILifecycleListener { + public: + virtual ~ILifecycleListener() = default; + // Called on commitChanges when a layer is added. The callback includes + // the layer state the client was created with as well as any state updates + // until changes were committed. + virtual void onLayerAdded(const RequestedLayerState&) = 0; + // Called on commitChanges when a layer has been destroyed. The callback + // includes the final state before the layer was destroyed. + virtual void onLayerDestroyed(const RequestedLayerState&) = 0; + }; + void addLifecycleListener(std::shared_ptr<ILifecycleListener>); + void removeLifecycleListener(std::shared_ptr<ILifecycleListener>); + const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const; + const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const; + const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const; + +private: + friend class LayerLifecycleManagerTest; + friend class HierarchyBuilderTest; + friend class android::SurfaceFlinger; + + RequestedLayerState* getLayerFromId(uint32_t); + std::vector<uint32_t>* getLinkedLayersFromId(uint32_t); + uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink); + uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer); + + struct References { + // Lifetime tied to mLayers + RequestedLayerState& owner; + std::vector<uint32_t> references; + std::string getDebugString() const; + }; + std::unordered_map<uint32_t, References> mIdToLayer; + // Listeners are invoked once changes are committed. + std::vector<std::shared_ptr<ILifecycleListener>> mListeners; + + // Aggregation of changes since last commit. + ftl::Flags<RequestedLayerState::Changes> mGlobalChanges; + std::vector<std::unique_ptr<RequestedLayerState>> mLayers; + // Layers pending destruction. Layers will be destroyed once changes are committed. + std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp new file mode 100644 index 0000000000..054382cd06 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -0,0 +1,370 @@ +/* + * Copyright 2022 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 "FrontEnd/LayerCreationArgs.h" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#undef LOG_TAG +#define LOG_TAG "RequestedLayerState" + +#include <private/android_filesystem_config.h> +#include <sys/types.h> + +#include "Layer.h" +#include "LayerHandle.h" +#include "RequestedLayerState.h" + +namespace android::surfaceflinger::frontend { +using ftl::Flags; +using namespace ftl::flag_operators; + +namespace { +uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) { + if (!surfaceControl) { + return UNASSIGNED_LAYER_ID; + } + + return LayerHandle::getLayerId(surfaceControl->getHandle()); +} + +std::string layerIdToString(uint32_t layerId) { + return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId); +} + +} // namespace + +RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) + : id(args.sequence), + name(args.name), + canBeRoot(args.addToRoot), + layerCreationFlags(args.flags), + textureName(args.textureName), + ownerUid(args.ownerUid), + ownerPid(args.ownerPid) { + layerId = static_cast<int32_t>(args.sequence); + changes |= RequestedLayerState::Changes::Created; + metadata.merge(args.metadata); + changes |= RequestedLayerState::Changes::Metadata; + handleAlive = true; + parentId = LayerHandle::getLayerId(args.parentHandle.promote()); + mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); + if (mirrorId != UNASSIGNED_LAYER_ID) { + changes |= RequestedLayerState::Changes::Mirror; + } + + flags = 0; + if (args.flags & ISurfaceComposerClient::eHidden) flags |= layer_state_t::eLayerHidden; + if (args.flags & ISurfaceComposerClient::eOpaque) flags |= layer_state_t::eLayerOpaque; + if (args.flags & ISurfaceComposerClient::eSecure) flags |= layer_state_t::eLayerSecure; + if (args.flags & ISurfaceComposerClient::eSkipScreenshot) { + flags |= layer_state_t::eLayerSkipScreenshot; + } + premultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); + potentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; + protectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; + if (args.flags & ISurfaceComposerClient::eNoColorFill) { + // Set an invalid color so there is no color fill. + // (b/259981098) use an explicit flag instead of relying on invalid values. + color.r = -1.0_hf; + color.g = -1.0_hf; + color.b = -1.0_hf; + } else { + color.rgb = {0.0_hf, 0.0_hf, 0.0_hf}; + } + color.a = 1.0f; + + crop.makeInvalid(); + z = 0; + layerStack = ui::DEFAULT_LAYER_STACK; + transformToDisplayInverse = false; + dataspace = ui::Dataspace::UNKNOWN; + dataspaceRequested = false; + hdrMetadata.validTypes = 0; + surfaceDamageRegion = Region::INVALID_REGION; + cornerRadius = 0.0f; + backgroundBlurRadius = 0; + api = -1; + hasColorTransform = false; + bufferTransform = 0; + requestedTransform.reset(); + bufferData = std::make_shared<BufferData>(); + bufferData->frameNumber = 0; + bufferData->acquireFence = sp<Fence>::make(-1); + acquireFenceTime = std::make_shared<FenceTime>(bufferData->acquireFence); + colorSpaceAgnostic = false; + frameRateSelectionPriority = Layer::PRIORITY_UNSET; + shadowRadius = 0.f; + fixedTransformHint = ui::Transform::ROT_INVALID; + destinationFrame.makeInvalid(); + isTrustedOverlay = false; + dropInputMode = gui::DropInputMode::NONE; + dimmingEnabled = true; + defaultFrameRateCompatibility = + static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default); + dataspace = ui::Dataspace::V0_SRGB; +} + +void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { + bool oldFlags = flags; + Rect oldBufferSize = getBufferSize(); + const layer_state_t& clientState = resolvedComposerState.state; + + uint64_t clientChanges = what | layer_state_t::diff(clientState); + layer_state_t::merge(clientState); + what = clientChanges; + + if (clientState.what & layer_state_t::eFlagsChanged) { + if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) { + changes |= RequestedLayerState::Changes::Visibility; + } + if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) { + changes |= RequestedLayerState::Changes::Geometry; + } + } + if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize()) { + changes |= RequestedLayerState::Changes::Geometry; + } + if (clientChanges & layer_state_t::HIERARCHY_CHANGES) + changes |= RequestedLayerState::Changes::Hierarchy; + if (clientChanges & layer_state_t::CONTENT_CHANGES) + changes |= RequestedLayerState::Changes::Content; + if (clientChanges & layer_state_t::GEOMETRY_CHANGES) + changes |= RequestedLayerState::Changes::Geometry; + + if (clientState.what & layer_state_t::eColorTransformChanged) { + static const mat4 identityMatrix = mat4(); + hasColorTransform = colorTransform != identityMatrix; + } + if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) { + changes |= RequestedLayerState::Changes::Z; + } + if (clientState.what & layer_state_t::eReparent) { + changes |= RequestedLayerState::Changes::Parent; + parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild); + parentSurfaceControlForChild = nullptr; + // Once a layer has be reparented, it cannot be placed at the root. It sounds odd + // but thats the existing logic and until we make this behavior more explicit, we need + // to maintain this logic. + canBeRoot = false; + } + if (clientState.what & layer_state_t::eRelativeLayerChanged) { + changes |= RequestedLayerState::Changes::RelativeParent; + relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl); + isRelativeOf = true; + relativeLayerSurfaceControl = nullptr; + } + if ((clientState.what & layer_state_t::eLayerChanged || + (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) && + isRelativeOf) { + // clear out relz data + relativeParentId = UNASSIGNED_LAYER_ID; + isRelativeOf = false; + changes |= RequestedLayerState::Changes::RelativeParent; + } + if (clientState.what & layer_state_t::eReparent && parentId == relativeParentId) { + // provide a hint that we are are now a direct child and not a relative child. + changes |= RequestedLayerState::Changes::RelativeParent; + } + if (clientState.what & layer_state_t::eInputInfoChanged) { + wp<IBinder>& touchableRegionCropHandle = + windowInfoHandle->editInfo()->touchableRegionCropHandle; + touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote()); + changes |= RequestedLayerState::Changes::Input; + touchableRegionCropHandle.clear(); + } + if (clientState.what & layer_state_t::eStretchChanged) { + stretchEffect.sanitize(); + } + + if (clientState.what & layer_state_t::eHasListenerCallbacksChanged) { + // TODO(b/238781169) handle callbacks + } + + if (clientState.what & layer_state_t::eBufferChanged) { + externalTexture = resolvedComposerState.externalTexture; + } + + if (clientState.what & layer_state_t::ePositionChanged) { + requestedTransform.set(x, y); + } + + if (clientState.what & layer_state_t::eMatrixChanged) { + requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + } +} + +ui::Transform RequestedLayerState::getTransform() const { + if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) { + // If destination frame is not set, use the requested transform set via + // Transaction::setPosition and Transaction::setMatrix. + return requestedTransform; + } + + Rect destRect = destinationFrame; + int32_t destW = destRect.width(); + int32_t destH = destRect.height(); + if (destRect.left < 0) { + destRect.left = 0; + destRect.right = destW; + } + if (destRect.top < 0) { + destRect.top = 0; + destRect.bottom = destH; + } + + if (!externalTexture) { + ui::Transform transform; + transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top)); + return transform; + } + + uint32_t bufferWidth = externalTexture->getWidth(); + uint32_t bufferHeight = externalTexture->getHeight(); + // Undo any transformations on the buffer. + if (bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + // TODO(b/238781169) remove dep + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (transformToDisplayInverse) { + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + float sx = static_cast<float>(destW) / static_cast<float>(bufferWidth); + float sy = static_cast<float>(destH) / static_cast<float>(bufferHeight); + ui::Transform transform; + transform.set(sx, 0, 0, sy); + transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top)); + return transform; +} + +std::string RequestedLayerState::getDebugString() const { + return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) + + ",relativeParent=" + layerIdToString(relativeParentId) + + ",isRelativeOf=" + std::to_string(isRelativeOf) + + ",mirrorId=" + layerIdToString(mirrorId) + + ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z); +} + +std::string RequestedLayerState::getDebugStringShort() const { + return "[" + std::to_string(id) + "]" + name; +} + +bool RequestedLayerState::canBeDestroyed() const { + return !handleAlive && parentId == UNASSIGNED_LAYER_ID; +} +bool RequestedLayerState::isRoot() const { + return canBeRoot && parentId == UNASSIGNED_LAYER_ID; +} +bool RequestedLayerState::isHiddenByPolicy() const { + return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden; +}; +half4 RequestedLayerState::getColor() const { + if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + return {0._hf, 0._hf, 0._hf, color.a}; + } + return color; +} +Rect RequestedLayerState::getBufferSize() const { + // for buffer state layers we use the display frame size as the buffer size. + if (!externalTexture) { + return Rect::INVALID_RECT; + } + + uint32_t bufWidth = externalTexture->getWidth(); + uint32_t bufHeight = externalTexture->getHeight(); + + // Undo any transformations on the buffer and return the result. + if (bufferTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + if (transformToDisplayInverse) { + // TODO(b/238781169) pass in display metrics (would be useful for input info as well + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + } + + return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); +} + +Rect RequestedLayerState::getCroppedBufferSize() const { + Rect size = getBufferSize(); + if (!crop.isEmpty() && size.isValid()) { + size.intersect(crop, &size); + } else if (!crop.isEmpty()) { + size = crop; + } + return size; +} + +Rect RequestedLayerState::getBufferCrop() const { + // this is the crop rectangle that applies to the buffer + // itself (as opposed to the window) + if (!bufferCrop.isEmpty()) { + // if the buffer crop is defined, we use that + return bufferCrop; + } else if (externalTexture != nullptr) { + // otherwise we use the whole buffer + return externalTexture->getBounds(); + } else { + // if we don't have a buffer yet, we use an empty/invalid crop + return Rect(); + } +} + +aidl::android::hardware::graphics::composer3::Composition RequestedLayerState::getCompositionType() + const { + using aidl::android::hardware::graphics::composer3::Composition; + // TODO(b/238781169) check about sidestream ready flag + if (sidebandStream.get()) { + return Composition::SIDEBAND; + } + if (!externalTexture) { + return Composition::SOLID_COLOR; + } + if (flags & layer_state_t::eLayerIsDisplayDecoration) { + return Composition::DISPLAY_DECORATION; + } + if (potentialCursor) { + return Composition::CURSOR; + } + return Composition::DEVICE; +} + +Rect RequestedLayerState::reduce(const Rect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + if (exclude.isRect()) { + return win.reduce(exclude.getBounds()); + } + return Region(win).subtract(exclude).getBounds(); +} + +// Returns true if the layer has a relative parent that is not its own parent. This is an input +// error from the client, and this check allows us to handle it gracefully. If both parentId and +// relativeParentId is unassigned then the layer does not have a valid relative parent. +// If the relative parentid is unassigned, the layer will be considered relative but won't be +// reachable. +bool RequestedLayerState::hasValidRelativeParent() const { + return isRelativeOf && parentId != relativeParentId; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h new file mode 100644 index 0000000000..78491655be --- /dev/null +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -0,0 +1,102 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <aidl/android/hardware/graphics/composer3/Composition.h> +#include <ftl/flags.h> +#include <gui/LayerState.h> +#include <renderengine/ExternalTexture.h> + +#include "LayerCreationArgs.h" +#include "TransactionState.h" + +namespace android::surfaceflinger::frontend { + +// Stores client requested states for a layer. +// This struct does not store any other states or states pertaining to +// other layers. Links to other layers that are part of the client +// requested state such as parent are translated to layer id so +// we can avoid extending the lifetime of layer handles. +struct RequestedLayerState : layer_state_t { + // Changes in state after merging with new state. This includes additional state + // changes found in layer_state_t::what. + enum class Changes : uint32_t { + Created = 1u << 0, + Destroyed = 1u << 1, + Hierarchy = 1u << 2, + Geometry = 1u << 3, + Content = 1u << 4, + Input = 1u << 5, + Z = 1u << 6, + Mirror = 1u << 7, + Parent = 1u << 8, + RelativeParent = 1u << 9, + Metadata = 1u << 10, + Visibility = 1u << 11, + }; + static Rect reduce(const Rect& win, const Region& exclude); + RequestedLayerState(const LayerCreationArgs&); + void merge(const ResolvedComposerState&); + ui::Transform getTransform() const; + bool canBeDestroyed() const; + bool isRoot() const; + bool isHiddenByPolicy() const; + half4 getColor() const; + Rect getBufferSize() const; + Rect getCroppedBufferSize() const; + Rect getBufferCrop() const; + std::string getDebugString() const; + std::string getDebugStringShort() const; + aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; + bool hasValidRelativeParent() const; + + // Layer serial number. This gives layers an explicit ordering, so we + // have a stable sort order when their layer stack and Z-order are + // the same. + const uint32_t id; + const std::string name; + bool canBeRoot = false; + const uint32_t layerCreationFlags; + const uint32_t textureName; + // The owner of the layer. If created from a non system process, it will be the calling uid. + // If created from a system process, the value can be passed in. + const uid_t ownerUid; + // The owner pid of the layer. If created from a non system process, it will be the calling pid. + // If created from a system process, the value can be passed in. + const pid_t ownerPid; + bool dataspaceRequested; + bool hasColorTransform; + bool premultipliedAlpha{true}; + // This layer can be a cursor on some displays. + bool potentialCursor{false}; + bool protectedByApp{false}; // application requires protected path to external sink + ui::Transform requestedTransform; + std::shared_ptr<FenceTime> acquireFenceTime; + std::shared_ptr<renderengine::ExternalTexture> externalTexture; + + // book keeping states + bool handleAlive = true; + bool isRelativeOf = false; + uint32_t parentId = UNASSIGNED_LAYER_ID; + uint32_t relativeParentId = UNASSIGNED_LAYER_ID; + uint32_t mirrorId = UNASSIGNED_LAYER_ID; + uint32_t touchCropId = UNASSIGNED_LAYER_ID; + uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID; + ftl::Flags<RequestedLayerState::Changes> changes; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h new file mode 100644 index 0000000000..f672f998d9 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/SwapErase.h @@ -0,0 +1,45 @@ +/* + * Copyright 2022 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 <vector> + +namespace android::surfaceflinger::frontend { +// Erases the first element in vec that matches value. This is a more optimal way to +// remove an element from a vector that avoids relocating all the elements after the one +// that is erased. +template <typename T> +void swapErase(std::vector<T>& vec, const T& value) { + auto it = std::find(vec.begin(), vec.end(), value); + if (it != vec.end()) { + std::iter_swap(it, vec.end() - 1); + vec.erase(vec.end() - 1); + } +} + +// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element +// that returns true for predicate. +template <typename T, class P> +void swapErase(std::vector<T>& vec, P predicate) { + auto it = std::find_if(vec.begin(), vec.end(), predicate); + if (it != vec.end()) { + std::iter_swap(it, vec.end() - 1); + vec.erase(vec.end() - 1); + } +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp new file mode 100644 index 0000000000..c2109b3aa5 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2022 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 "TransactionHandler" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <cutils/trace.h> +#include <utils/Log.h> + +#include "TransactionHandler.h" + +namespace android::surfaceflinger::frontend { + +void TransactionHandler::queueTransaction(TransactionState&& state) { + mLocklessTransactionQueue.push(std::move(state)); + mPendingTransactionCount.fetch_add(1); + ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); +} + +std::vector<TransactionState> TransactionHandler::flushTransactions() { + while (!mLocklessTransactionQueue.isEmpty()) { + auto maybeTransaction = mLocklessTransactionQueue.pop(); + if (!maybeTransaction.has_value()) { + break; + } + auto transaction = maybeTransaction.value(); + mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction)); + } + + // Collect transaction that are ready to be applied. + std::vector<TransactionState> transactions; + TransactionFlushState flushState; + flushState.queueProcessTime = systemTime(); + // Transactions with a buffer pending on a barrier may be on a different applyToken + // than the transaction which satisfies our barrier. In fact this is the exact use case + // that the primitive is designed for. This means we may first process + // the barrier dependent transaction, determine it ineligible to complete + // and then satisfy in a later inner iteration of flushPendingTransactionQueues. + // The barrier dependent transaction was eligible to be presented in this frame + // but we would have prevented it without case. To fix this we continually + // loop through flushPendingTransactionQueues until we perform an iteration + // where the number of transactionsPendingBarrier doesn't change. This way + // we can continue to resolve dependency chains of barriers as far as possible. + int lastTransactionsPendingBarrier = 0; + int transactionsPendingBarrier = 0; + do { + lastTransactionsPendingBarrier = transactionsPendingBarrier; + // Collect transactions that are ready to be applied. + transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState); + } while (lastTransactionsPendingBarrier != transactionsPendingBarrier); + + mPendingTransactionCount.fetch_sub(transactions.size()); + ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); + return transactions; +} + +TransactionHandler::TransactionReadiness TransactionHandler::applyFilters( + TransactionFlushState& flushState) { + auto ready = TransactionReadiness::Ready; + for (auto& filter : mTransactionReadyFilters) { + auto perFilterReady = filter(flushState); + switch (perFilterReady) { + case TransactionReadiness::NotReady: + case TransactionReadiness::NotReadyBarrier: + return perFilterReady; + + case TransactionReadiness::ReadyUnsignaled: + case TransactionReadiness::ReadyUnsignaledSingle: + // If one of the filters allows latching an unsignaled buffer, latch this ready + // state. + ready = perFilterReady; + break; + case TransactionReadiness::Ready: + continue; + } + } + return ready; +} + +int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions, + TransactionFlushState& flushState) { + int transactionsPendingBarrier = 0; + auto it = mPendingTransactionQueues.begin(); + while (it != mPendingTransactionQueues.end()) { + auto& queue = it->second; + IBinder* queueToken = it->first.get(); + + // if we have already flushed a transaction with an unsignaled buffer then stop queue + // processing + if (std::find(flushState.queuesWithUnsignaledBuffers.begin(), + flushState.queuesWithUnsignaledBuffers.end(), + queueToken) != flushState.queuesWithUnsignaledBuffers.end()) { + continue; + } + + while (!queue.empty()) { + auto& transaction = queue.front(); + flushState.transaction = &transaction; + auto ready = applyFilters(flushState); + if (ready == TransactionReadiness::NotReadyBarrier) { + transactionsPendingBarrier++; + break; + } else if (ready == TransactionReadiness::NotReady) { + break; + } + + // Transaction is ready move it from the pending queue. + flushState.firstTransaction = false; + removeFromStalledTransactions(transaction.id); + transactions.emplace_back(std::move(transaction)); + queue.pop(); + + // If the buffer is unsignaled, then we don't want to signal other transactions using + // the buffer as a barrier. + auto& readyToApplyTransaction = transactions.back(); + if (ready == TransactionReadiness::Ready) { + readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) { + const bool frameNumberChanged = state.bufferData->flags.test( + BufferData::BufferDataChange::frameNumberChanged); + if (frameNumberChanged) { + flushState.bufferLayersReadyToPresent + .emplace_or_replace(state.surface.get(), + state.bufferData->frameNumber); + } else { + // Barrier function only used for BBQ which always includes a frame number. + // This value only used for barrier logic. + flushState.bufferLayersReadyToPresent + .emplace_or_replace(state.surface.get(), + std::numeric_limits<uint64_t>::max()); + } + }); + } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) { + // Track queues with a flushed unsingaled buffer. + flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken); + break; + } + } + + if (queue.empty()) { + it = mPendingTransactionQueues.erase(it); + } else { + it = std::next(it, 1); + } + } + return transactionsPendingBarrier; +} + +void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) { + mTransactionReadyFilters.emplace_back(std::move(filter)); +} + +bool TransactionHandler::hasPendingTransactions() { + return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty(); +} + +void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId, + sp<ITransactionCompletedListener>& listener, + const std::string& reason) { + if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) != + mStalledTransactions.end()) { + return; + } + + mStalledTransactions.push_back(transactionId); + listener->onTransactionQueueStalled(reason); +} + +void TransactionHandler::removeFromStalledTransactions(uint64_t id) { + auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id); + if (it != mStalledTransactions.end()) { + mStalledTransactions.erase(it); + } +} +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h new file mode 100644 index 0000000000..475ff1b1dc --- /dev/null +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -0,0 +1,77 @@ +/* + * Copyright 2022 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 <semaphore.h> +#include <cstdint> +#include <vector> + +#include <LocklessQueue.h> +#include <TransactionState.h> +#include <android-base/thread_annotations.h> +#include <ftl/small_map.h> +#include <ftl/small_vector.h> + +namespace android { + +class TestableSurfaceFlinger; +using gui::IListenerHash; +namespace surfaceflinger::frontend { + +class TransactionHandler { +public: + struct TransactionFlushState { + const TransactionState* transaction; + bool firstTransaction = true; + nsecs_t queueProcessTime = 0; + // Layer handles that have transactions with buffers that are ready to be applied. + ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15> + bufferLayersReadyToPresent = {}; + ftl::SmallVector<IBinder* /* queueToken */, 15> queuesWithUnsignaledBuffers; + }; + enum class TransactionReadiness { + NotReady, + NotReadyBarrier, + Ready, + ReadyUnsignaled, + ReadyUnsignaledSingle, + }; + using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>; + + bool hasPendingTransactions(); + std::vector<TransactionState> flushTransactions(); + void addTransactionReadyFilter(TransactionFilter&&); + void queueTransaction(TransactionState&&); + void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&, + const std::string& reason); + void removeFromStalledTransactions(uint64_t transactionId); + +private: + // For unit tests + friend class ::android::TestableSurfaceFlinger; + + int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&); + TransactionReadiness applyFilters(TransactionFlushState&); + std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> + mPendingTransactionQueues; + LocklessQueue<TransactionState> mLocklessTransactionQueue; + std::atomic<size_t> mPendingTransactionCount = 0; + ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters; + std::vector<uint64_t> mStalledTransactions; +}; +} // namespace surfaceflinger::frontend +} // namespace android diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp index c06e300cdc..c88554ee52 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.cpp +++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp @@ -51,7 +51,7 @@ void HdrLayerInfoReporter::binderDied(const wp<IBinder>& who) { void HdrLayerInfoReporter::addListener(const sp<gui::IHdrLayerInfoListener>& listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); - asBinder->linkToDeath(this); + asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); std::lock_guard lock(mMutex); mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, HdrLayerInfo{}}); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c2b0a112d6..b7abd95af8 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -29,6 +29,7 @@ #include <android-base/stringprintf.h> #include <android/native_window.h> #include <binder/IPCThreadState.h> +#include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> @@ -41,6 +42,7 @@ #include <gui/BufferItem.h> #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> +#include <gui/TraceUtils.h> #include <math.h> #include <private/android_filesystem_config.h> #include <renderengine/RenderEngine.h> @@ -60,66 +62,103 @@ #include <algorithm> #include <mutex> +#include <optional> #include <sstream> -#include "BufferLayer.h" -#include "Colorizer.h" #include "DisplayDevice.h" #include "DisplayHardware/HWComposer.h" -#include "EffectLayer.h" #include "FrameTimeline.h" #include "FrameTracer/FrameTracer.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHandle.h" #include "LayerProtoHelper.h" -#include "LayerRejecter.h" -#include "MonitoredProducer.h" #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" #define DEBUG_RESIZE 0 +#define EARLY_RELEASE_ENABLED false namespace android { namespace { constexpr int kDumpTableRowLength = 159; + const ui::Transform kIdentityTransform; + +bool assignTransform(ui::Transform* dst, ui::Transform& from) { + if (*dst == from) { + return false; + } + *dst = from; + return true; +} + +TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { + using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; + using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; + const auto frameRateCompatibility = [frameRate] { + switch (frameRate.type) { + case Layer::FrameRateCompatibility::Default: + return FrameRateCompatibility::Default; + case Layer::FrameRateCompatibility::ExactOrMultiple: + return FrameRateCompatibility::ExactOrMultiple; + default: + return FrameRateCompatibility::Undefined; + } + }(); + + const auto seamlessness = [frameRate] { + switch (frameRate.seamlessness) { + case scheduler::Seamlessness::OnlySeamless: + return Seamlessness::ShouldBeSeamless; + case scheduler::Seamlessness::SeamedAndSeamless: + return Seamlessness::NotRequired; + default: + return Seamlessness::Undefined; + } + }(); + + return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(), + .frameRateCompatibility = frameRateCompatibility, + .seamlessness = seamlessness}; +} + } // namespace using namespace ftl::flag_operators; using base::StringAppendF; +using gui::GameMode; +using gui::LayerMetadata; using gui::WindowInfo; using PresentState = frametimeline::SurfaceFrame::PresentState; -std::atomic<int32_t> Layer::sSequence{1}; - Layer::Layer(const LayerCreationArgs& args) - : sequence(args.sequence.value_or(sSequence++)), - mFlinger(args.flinger), + : sequence(args.sequence), + mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)), mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)), mClientRef(args.client), - mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))), - mLayerCreationFlags(args.flags) { + mWindowType(static_cast<WindowInfo::Type>( + args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), + mLayerCreationFlags(args.flags), + mBorderEnabled(false), + mTextureName(args.textureName), + mLayerFE(args.flinger->getFactory().createLayerFE(mName)) { + ALOGV("Creating Layer %s", getDebugName()); + uint32_t layerFlags = 0; if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure; if (args.flags & ISurfaceComposerClient::eSkipScreenshot) layerFlags |= layer_state_t::eLayerSkipScreenshot; - if (args.sequence) { - sSequence = *args.sequence + 1; - } mDrawingState.flags = layerFlags; - mDrawingState.active_legacy.transform.set(0, 0); mDrawingState.crop.makeInvalid(); - mDrawingState.requestedCrop = mDrawingState.crop; mDrawingState.z = 0; mDrawingState.color.a = 1.0f; mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK; mDrawingState.sequence = 0; - mDrawingState.requested_legacy = mDrawingState.active_legacy; - mDrawingState.width = UINT32_MAX; - mDrawingState.height = UINT32_MAX; mDrawingState.transform.set(0, 0); mDrawingState.frameNumber = 0; mDrawingState.bufferTransform = 0; @@ -128,6 +167,7 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.acquireFence = sp<Fence>::make(-1); mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); mDrawingState.dataspace = ui::Dataspace::UNKNOWN; + mDrawingState.dataspaceRequested = false; mDrawingState.hdrMetadata.validTypes = 0; mDrawingState.surfaceDamageRegion = Region::INVALID_REGION; mDrawingState.cornerRadius = 0.0f; @@ -145,6 +185,7 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.isTrustedOverlay = false; mDrawingState.dropInputMode = gui::DropInputMode::NONE; mDrawingState.dimmingEnabled = true; + mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default; if (args.flags & ISurfaceComposerClient::eNoColorFill) { // Set an invalid color so there is no color fill. @@ -152,22 +193,22 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.color.g = -1.0_hf; mDrawingState.color.b = -1.0_hf; } - CompositorTiming compositorTiming; - args.flinger->getCompositorTiming(&compositorTiming); - mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval); - mCallingPid = args.callingPid; - mCallingUid = args.callingUid; + mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod()); - if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) { - // If the system didn't send an ownerUid, use the callingUid for the ownerUid. - mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid); - mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid); - } else { - // A create layer request from a non system request cannot specify the owner uid - mOwnerUid = mCallingUid; - mOwnerPid = mCallingPid; - } + mOwnerUid = args.ownerUid; + mOwnerPid = args.ownerPid; + + mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); + mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; + mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; + mDrawingState.dataspace = ui::Dataspace::V0_SRGB; + + mSnapshot->sequence = sequence; + mSnapshot->name = getDebugName(); + mSnapshot->textureName = mTextureName; + mSnapshot->premultipliedAlpha = mPremultipliedAlpha; + mSnapshot->transform = {}; } void Layer::onFirstRef() { @@ -175,10 +216,27 @@ void Layer::onFirstRef() { } Layer::~Layer() { - sp<Client> c(mClientRef.promote()); - if (c != 0) { - c->detachLayer(this); + // The original layer and the clone layer share the same texture and buffer. Therefore, only + // one of the layers, in this case the original layer, needs to handle the deletion. The + // original layer and the clone should be removed at the same time so there shouldn't be any + // issue with the clone layer trying to use the texture. + if (mBufferInfo.mBuffer != nullptr) { + callReleaseBufferCallback(mDrawingState.releaseBufferListener, + mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, + mBufferInfo.mFence, + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( + mOwnerUid)); + } + if (!isClone()) { + // The original layer and the clone layer share the same texture. Therefore, only one of + // the layers, in this case the original layer, needs to handle the deletion. The original + // layer and the clone should be removed at the same time so there shouldn't be any issue + // with the clone layer trying to use the deleted texture. + mFlinger->deleteTextureAsync(mTextureName); } + const int32_t layerId = getSequence(); + mFlinger->mTimeStats->onDestroy(layerId); + mFlinger->mFrameTracer->onDestroy(layerId); mFrameTracker.logAndResetStats(mName); mFlinger->onLayerDestroyed(this); @@ -191,29 +249,10 @@ Layer::~Layer() { } } -LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name, - uint32_t flags, LayerMetadata metadata) - : flinger(flinger), - client(std::move(client)), - name(std::move(name)), - flags(flags), - metadata(std::move(metadata)) { - IPCThreadState* ipc = IPCThreadState::self(); - callingPid = ipc->getCallingPid(); - callingUid = ipc->getCallingUid(); -} - // --------------------------------------------------------------------------- // callbacks // --------------------------------------------------------------------------- -/* - * onLayerDisplayed is only meaningful for BufferLayer, but, is called through - * Layer. So, the implementation is done in BufferLayer. When called on a - * EffectLayer object, it's essentially a NOP. - */ -void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult>) {} - void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) { if (mDrawingState.zOrderRelativeOf == nullptr) { return; @@ -226,7 +265,7 @@ void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) { } if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) { - strongRelative->removeZOrderRelative(this); + strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); mFlinger->setTransactionFlags(eTraversalNeeded); setZOrderRelativeOf(nullptr); } @@ -238,13 +277,13 @@ void Layer::removeFromCurrentState() { mFlinger->mScheduler->deregisterLayer(this); } - mFlinger->markLayerPendingRemovalLocked(this); + mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this)); } sp<Layer> Layer::getRootLayer() { sp<Layer> parent = getParent(); if (parent == nullptr) { - return this; + return sp<Layer>::fromExisting(this); } return parent->getRootLayer(); } @@ -287,7 +326,7 @@ sp<IBinder> Layer::getHandle() { return nullptr; } mGetHandleCalled = true; - return new Handle(mFlinger, this); + return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this)); } // --------------------------------------------------------------------------- @@ -379,6 +418,10 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, for (const sp<Layer>& child : mDrawingChildren) { child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius); } + + if (mPotentialCursor) { + prepareCursorCompositionState(); + } } Rect Layer::getCroppedBufferSize(const State& s) const { @@ -414,40 +457,40 @@ void Layer::prepareBasicGeometryCompositionState() { : Hwc2::IComposerClient::BlendMode::COVERAGE; } - auto* compositionState = editCompositionState(); - compositionState->outputFilter = getOutputFilter(); - compositionState->isVisible = isVisible(); - compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; - compositionState->shadowRadius = mEffectiveShadowRadius; + auto* snapshot = editLayerSnapshot(); + snapshot->outputFilter = getOutputFilter(); + snapshot->isVisible = isVisible(); + snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; + snapshot->shadowRadius = mEffectiveShadowRadius; - compositionState->contentDirty = contentDirty; + snapshot->contentDirty = contentDirty; contentDirty = false; - compositionState->geomLayerBounds = mBounds; - compositionState->geomLayerTransform = getTransform(); - compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse(); - compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState); - - compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - compositionState->alpha = alpha; - compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; - compositionState->blurRegions = drawingState.blurRegions; - compositionState->stretchEffect = getStretchEffect(); + snapshot->geomLayerBounds = mBounds; + snapshot->geomLayerTransform = getTransform(); + snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse(); + snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState); + snapshot->blurRegionTransform = getActiveTransform(drawingState).inverse(); + snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); + snapshot->alpha = alpha; + snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius; + snapshot->blurRegions = drawingState.blurRegions; + snapshot->stretchEffect = getStretchEffect(); } void Layer::prepareGeometryCompositionState() { const auto& drawingState{getDrawingState()}; - auto* compositionState = editCompositionState(); + auto* snapshot = editLayerSnapshot(); - compositionState->geomBufferSize = getBufferSize(drawingState); - compositionState->geomContentCrop = getBufferCrop(); - compositionState->geomCrop = getCrop(drawingState); - compositionState->geomBufferTransform = getBufferTransform(); - compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); - compositionState->geomUsesSourceCrop = usesSourceCrop(); - compositionState->isSecure = isSecure(); + snapshot->geomBufferSize = getBufferSize(drawingState); + snapshot->geomContentCrop = getBufferCrop(); + snapshot->geomCrop = getCrop(drawingState); + snapshot->geomBufferTransform = getBufferTransform(); + snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); + snapshot->geomUsesSourceCrop = usesSourceCrop(); + snapshot->isSecure = isSecure(); - compositionState->metadata.clear(); + snapshot->metadata.clear(); const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata(); for (const auto& [key, mandatory] : supportedMetadata) { const auto& genericLayerMetadataCompatibilityMap = @@ -463,50 +506,87 @@ void Layer::prepareGeometryCompositionState() { continue; } - compositionState->metadata - .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second}); + snapshot->metadata.emplace(key, + compositionengine::GenericLayerMetadataEntry{mandatory, + it->second}); } } void Layer::preparePerFrameCompositionState() { const auto& drawingState{getDrawingState()}; - auto* compositionState = editCompositionState(); + auto* snapshot = editLayerSnapshot(); - compositionState->forceClientComposition = false; + snapshot->forceClientComposition = false; - compositionState->isColorspaceAgnostic = isColorSpaceAgnostic(); - compositionState->dataspace = getDataSpace(); - compositionState->colorTransform = getColorTransform(); - compositionState->colorTransformIsIdentity = !hasColorTransform(); - compositionState->surfaceDamage = surfaceDamageRegion; - compositionState->hasProtectedContent = isProtected(); - compositionState->dimmingEnabled = isDimmingEnabled(); + snapshot->isColorspaceAgnostic = isColorSpaceAgnostic(); + snapshot->dataspace = getDataSpace(); + snapshot->colorTransform = getColorTransform(); + snapshot->colorTransformIsIdentity = !hasColorTransform(); + snapshot->surfaceDamage = surfaceDamageRegion; + snapshot->hasProtectedContent = isProtected(); + snapshot->dimmingEnabled = isDimmingEnabled(); const bool usesRoundedCorners = hasRoundedCorners(); - compositionState->isOpaque = - isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; + snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; // Force client composition for special cases known only to the front-end. // Rounded corners no longer force client composition, since we may use a // hole punch so that the layer will appear to have rounded corners. if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 || - compositionState->stretchEffect.hasEffect()) { - compositionState->forceClientComposition = true; + snapshot->stretchEffect.hasEffect()) { + snapshot->forceClientComposition = true; } // If there are no visible region changes, we still need to update blur parameters. - compositionState->blurRegions = drawingState.blurRegions; - compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; + snapshot->blurRegions = drawingState.blurRegions; + snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius; // Layer framerate is used in caching decisions. // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in // LayerFECompositionState where it would be visible to Flattener. - compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence()); + snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence()); + + if (hasBufferOrSidebandStream()) { + preparePerFrameBufferCompositionState(); + } else { + preparePerFrameEffectsCompositionState(); + } +} + +void Layer::preparePerFrameBufferCompositionState() { + // Sideband layers + auto* snapshot = editLayerSnapshot(); + if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) { + snapshot->compositionType = + aidl::android::hardware::graphics::composer3::Composition::SIDEBAND; + return; + } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) { + snapshot->compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + } else { + // Normal buffer layers + snapshot->hdrMetadata = mBufferInfo.mHdrMetadata; + snapshot->compositionType = mPotentialCursor + ? aidl::android::hardware::graphics::composer3::Composition::CURSOR + : aidl::android::hardware::graphics::composer3::Composition::DEVICE; + } + + snapshot->buffer = getBuffer(); + snapshot->acquireFence = mBufferInfo.mFence; + snapshot->frameNumber = mBufferInfo.mFrameNumber; + snapshot->sidebandStreamHasFrame = false; +} + +void Layer::preparePerFrameEffectsCompositionState() { + auto* snapshot = editLayerSnapshot(); + snapshot->color = getColor(); + snapshot->compositionType = + aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; } void Layer::prepareCursorCompositionState() { const State& drawingState{getDrawingState()}; - auto* compositionState = editCompositionState(); + auto* snapshot = editLayerSnapshot(); // Apply the layer's transform, followed by the display's global transform // Here we're guaranteed that the layer's transform preserves rects @@ -515,52 +595,7 @@ void Layer::prepareCursorCompositionState() { Rect bounds = reduce(win, getActiveTransparentRegion(drawingState)); Rect frame(getTransform().transform(bounds)); - compositionState->cursorFrame = frame; -} - -sp<compositionengine::LayerFE> Layer::asLayerFE() const { - return const_cast<compositionengine::LayerFE*>( - static_cast<const compositionengine::LayerFE*>(this)); -} - -sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const { - return nullptr; -} - -compositionengine::LayerFECompositionState* Layer::editCompositionState() { - return nullptr; -} - -const compositionengine::LayerFECompositionState* Layer::getCompositionState() const { - return nullptr; -} - -bool Layer::onPreComposition(nsecs_t) { - return false; -} - -void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) { - using StateSubset = compositionengine::LayerFE::StateSubset; - - switch (subset) { - case StateSubset::BasicGeometry: - prepareBasicGeometryCompositionState(); - break; - - case StateSubset::GeometryAndContent: - prepareBasicGeometryCompositionState(); - prepareGeometryCompositionState(); - preparePerFrameCompositionState(); - break; - - case StateSubset::Content: - preparePerFrameCompositionState(); - break; - - case StateSubset::Cursor: - prepareCursorCompositionState(); - break; - } + snapshot->cursorFrame = frame; } const char* Layer::getDebugName() const { @@ -571,109 +606,6 @@ const char* Layer::getDebugName() const { // drawing... // --------------------------------------------------------------------------- -std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { - if (!getCompositionState()) { - return {}; - } - - FloatRect bounds = getBounds(); - half alpha = getAlpha(); - - compositionengine::LayerFE::LayerSettings layerSettings; - layerSettings.geometry.boundaries = bounds; - layerSettings.geometry.positionTransform = getTransform().asMatrix4(); - - // skip drawing content if the targetSettings indicate the content will be occluded - const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent; - layerSettings.skipContentDraw = !drawContent; - - if (hasColorTransform()) { - layerSettings.colorTransform = getColorTransform(); - } - - const auto roundedCornerState = getRoundedCornerState(); - layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius; - layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect; - - layerSettings.alpha = alpha; - layerSettings.sourceDataspace = getDataSpace(); - - // Override the dataspace transfer from 170M to sRGB if the device configuration requests this. - // We do this here instead of in buffer info so that dumpsys can still report layers that are - // using the 170M transfer. - if (mFlinger->mTreat170mAsSrgb && - (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) == - HAL_DATASPACE_TRANSFER_SMPTE_170M) { - layerSettings.sourceDataspace = static_cast<ui::Dataspace>( - (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) | - (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) | - HAL_DATASPACE_TRANSFER_SRGB); - } - - layerSettings.whitePointNits = targetSettings.whitePointNits; - switch (targetSettings.blurSetting) { - case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled: - layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); - layerSettings.blurRegions = getBlurRegions(); - layerSettings.blurRegionTransform = - getActiveTransform(getDrawingState()).inverse().asMatrix4(); - break; - case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: - layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); - break; - case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly: - layerSettings.blurRegions = getBlurRegions(); - layerSettings.blurRegionTransform = - getActiveTransform(getDrawingState()).inverse().asMatrix4(); - break; - case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled: - default: - break; - } - layerSettings.stretchEffect = getStretchEffect(); - // Record the name of the layer for debugging further down the stack. - layerSettings.name = getName(); - return layerSettings; -} - -void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, - bool blackout) const { - layerSettings.source.buffer.buffer = nullptr; - layerSettings.source.solidColor = half3(0.0, 0.0, 0.0); - layerSettings.disableBlending = true; - layerSettings.bufferId = 0; - layerSettings.frameNumber = 0; - - // If layer is blacked out, force alpha to 1 so that we draw a black color layer. - layerSettings.alpha = blackout ? 1.0f : 0.0f; - layerSettings.name = getName(); -} - -// TODO(b/188891810): This method now only ever returns 0 or 1 layers so we should return -// std::optional instead of a vector. Additionally, we should consider removing -// this method entirely in favor of calling prepareClientComposition directly. -std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { - std::optional<compositionengine::LayerFE::LayerSettings> layerSettings = - prepareClientComposition(targetSettings); - // Nothing to render. - if (!layerSettings) { - return {}; - } - - // HWC requests to clear this layer. - if (targetSettings.clearContent) { - prepareClearClientComposition(*layerSettings, false /* blackout */); - return {*layerSettings}; - } - - // set the shadow for the layer if needed - prepareShadowClientComposition(*layerSettings, targetSettings.viewport); - - return {*layerSettings}; -} - aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType( const DisplayDevice& display) const { const auto outputLayer = findOutputLayerForDisplay(&display); @@ -721,7 +653,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { if (s.sequence != mLastCommittedTxSequence) { // invalidate and recompute the visible regions if needed - mLastCommittedTxSequence = s.sequence; + mLastCommittedTxSequence = s.sequence; flags |= eVisibleRegion; this->contentDirty = true; @@ -729,6 +661,10 @@ uint32_t Layer::doTransaction(uint32_t flags) { mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering(); } + if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) { + mFlinger->mUpdateInputInfo = true; + } + commitTransaction(mDrawingState); return flags; @@ -758,16 +694,6 @@ void Layer::setTransactionFlags(uint32_t mask) { mTransactionFlags |= mask; } -bool Layer::setPosition(float x, float y) { - if (mDrawingState.transform.tx() == x && mDrawingState.transform.ty() == y) return false; - mDrawingState.sequence++; - mDrawingState.transform.set(x, y); - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) { ssize_t idx = mCurrentChildren.indexOf(childLayer); if (idx < 0) { @@ -807,7 +733,7 @@ bool Layer::setLayer(int32_t z) { if (mDrawingState.zOrderRelativeOf != nullptr) { sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote(); if (strongRelative != nullptr) { - strongRelative->removeZOrderRelative(this); + strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); } setZOrderRelativeOf(nullptr); } @@ -839,7 +765,7 @@ void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { } bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - sp<Layer> relative = fromHandle(relativeToHandle).promote(); + sp<Layer> relative = LayerHandle::getLayer(relativeToHandle); if (relative == nullptr) { return false; } @@ -865,10 +791,10 @@ bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relati auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); if (oldZOrderRelativeOf != nullptr) { - oldZOrderRelativeOf->removeZOrderRelative(this); + oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); } setZOrderRelativeOf(relative); - relative->addZOrderRelative(this); + relative->addZOrderRelative(wp<Layer>::fromExisting(this)); setTransactionFlags(eTransactionNeeded); @@ -879,7 +805,7 @@ bool Layer::setTrustedOverlay(bool isTrustedOverlay) { if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false; mDrawingState.isTrustedOverlay = isTrustedOverlay; mDrawingState.modified = true; - mFlinger->mInputInfoChanged = true; + mFlinger->mUpdateInputInfo = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -892,20 +818,6 @@ bool Layer::isTrustedOverlay() const { return (p != nullptr) && p->isTrustedOverlay(); } -bool Layer::setSize(uint32_t w, uint32_t h) { - if (mDrawingState.requested_legacy.w == w && mDrawingState.requested_legacy.h == h) - return false; - mDrawingState.requested_legacy.w = w; - mDrawingState.requested_legacy.h = h; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - // record the new size, from this point on, when the client request - // a buffer, it'll get the new size. - setDefaultBufferSize(mDrawingState.requested_legacy.w, mDrawingState.requested_legacy.h); - return true; -} - bool Layer::setAlpha(float alpha) { if (mDrawingState.color.a == alpha) return false; mDrawingState.sequence++; @@ -975,20 +887,10 @@ bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { setTransactionFlags(eTransactionNeeded); return true; } -bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { - ui::Transform t; - t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - - mDrawingState.sequence++; - mDrawingState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - mDrawingState.modified = true; - - setTransactionFlags(eTransactionNeeded); - return true; -} bool Layer::setTransparentRegionHint(const Region& transparent) { - mDrawingState.requestedTransparentRegion_legacy = transparent; + mDrawingState.sequence++; + mDrawingState.transparentRegionHint = transparent; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -1017,9 +919,8 @@ bool Layer::setFlags(uint32_t flags, uint32_t mask) { } bool Layer::setCrop(const Rect& crop) { - if (mDrawingState.requestedCrop == crop) return false; + if (mDrawingState.crop == crop) return false; mDrawingState.sequence++; - mDrawingState.requestedCrop = crop; mDrawingState.crop = crop; mDrawingState.modified = true; @@ -1087,12 +988,27 @@ int32_t Layer::getFrameRateSelectionPriority() const { return Layer::PRIORITY_UNSET; } +bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) { + if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false; + mDrawingState.defaultFrameRateCompatibility = compatibility; + mDrawingState.modified = true; + mFlinger->mScheduler->setDefaultFrameRateCompatibility(this); + setTransactionFlags(eTransactionNeeded); + return true; +} + +scheduler::LayerInfo::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const { + return mDrawingState.defaultFrameRateCompatibility; +} + bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE; }; -ui::LayerStack Layer::getLayerStack() const { - if (const auto parent = mDrawingParent.promote()) { +ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const { + bool useDrawing = state == LayerVector::StateSet::Drawing; + const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); + if (parent) { return parent->getLayerStack(); } return getDrawingState().layerStack; @@ -1151,6 +1067,28 @@ StretchEffect Layer::getStretchEffect() const { return StretchEffect{}; } +bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) { + if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) { + return false; + } + mBorderEnabled = shouldEnable; + mBorderWidth = width; + mBorderColor = color; + return true; +} + +bool Layer::isBorderEnabled() { + return mBorderEnabled; +} + +float Layer::getBorderWidth() { + return mBorderWidth; +} + +const half4& Layer::getBorderColor() { + return mBorderColor; +} + bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) { // The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate const auto frameRate = [&] { @@ -1181,7 +1119,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for // the same reason we are allowing touch boost for those layers. See - // RefreshRateConfigs::getBestRefreshRate for more details. + // RefreshRateSelector::rankFrameRates for details. const auto layerVotedWithDefaultCompatibility = frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default; const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote; @@ -1193,7 +1131,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran void Layer::updateTreeHasFrameRateVote() { const auto root = [&]() -> sp<Layer> { - sp<Layer> layer = this; + sp<Layer> layer = sp<Layer>::fromExisting(this); while (auto parent = layer->getParent()) { layer = parent; } @@ -1324,7 +1262,6 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( if (fps) { surfaceFrame->setRenderRate(*fps); } - // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well. onSurfaceFrameCreated(surfaceFrame); return surfaceFrame; } @@ -1386,6 +1323,10 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const { return usage; } +void Layer::skipReportingTransformHint() { + mSkipReportingTransformHint = true; +} + void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) { if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) { transformHint = ui::Transform::ROT_0; @@ -1399,16 +1340,15 @@ void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) { // ---------------------------------------------------------------------------- // TODO(marissaw): add new layer state info to layer debugging -LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { +gui::LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { using namespace std::string_literals; - LayerDebugInfo info; + gui::LayerDebugInfo info; const State& ds = getDrawingState(); info.mName = getName(); sp<Layer> parent = mDrawingParent.promote(); info.mParentName = parent ? parent->getName() : "none"s; info.mType = getType(); - info.mTransparentRegion = ds.activeTransparentRegion_legacy; info.mVisibleRegion = getVisibleRegion(display); info.mSurfaceDamageRegion = surfaceDamageRegion; @@ -1416,8 +1356,6 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { info.mX = ds.transform.tx(); info.mY = ds.transform.ty(); info.mZ = ds.z; - info.mWidth = ds.width; - info.mHeight = ds.height; info.mCrop = ds.crop; info.mColor = ds.color; info.mFlags = ds.flags; @@ -1530,9 +1468,10 @@ void Layer::getFrameStats(FrameStats* outStats) const { mFrameTracker.getStats(outStats); } -void Layer::dumpCallingUidPid(std::string& result) const { - StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n", - getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid); +void Layer::dumpOffscreenDebugInfo(std::string& result) const { + std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : ""; + StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(), + mOwnerPid, mOwnerUid); } void Layer::onDisconnect() { @@ -1551,8 +1490,9 @@ size_t Layer::getChildrenCount() const { void Layer::setGameModeForTree(GameMode gameMode) { const auto& currentState = getDrawingState(); - if (currentState.metadata.has(METADATA_GAME_MODE)) { - gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0)); + if (currentState.metadata.has(gui::METADATA_GAME_MODE)) { + gameMode = + static_cast<GameMode>(currentState.metadata.getInt32(gui::METADATA_GAME_MODE, 0)); } setGameMode(gameMode); for (const sp<Layer>& child : mCurrentChildren) { @@ -1565,7 +1505,7 @@ void Layer::addChild(const sp<Layer>& layer) { setTransactionFlags(eTransactionNeeded); mCurrentChildren.add(layer); - layer->setParent(this); + layer->setParent(sp<Layer>::fromExisting(this)); layer->setGameModeForTree(mGameMode); updateTreeHasFrameRateVote(); } @@ -1597,7 +1537,7 @@ void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { bool Layer::reparent(const sp<IBinder>& newParentHandle) { sp<Layer> newParent; if (newParentHandle != nullptr) { - newParent = fromHandle(newParentHandle).promote(); + newParent = LayerHandle::getLayer(newParentHandle); if (newParent == nullptr) { ALOGE("Unable to promote Layer handle"); return false; @@ -1610,11 +1550,11 @@ bool Layer::reparent(const sp<IBinder>& newParentHandle) { sp<Layer> parent = getParent(); if (parent != nullptr) { - parent->removeChild(this); + parent->removeChild(sp<Layer>::fromExisting(this)); } if (newParentHandle != nullptr) { - newParent->addChild(this); + newParent->addChild(sp<Layer>::fromExisting(this)); if (!newParent->isRemovedFromCurrentState()) { addToCurrentState(); } else { @@ -1896,8 +1836,11 @@ half4 Layer::getColor() const { } int32_t Layer::getBackgroundBlurRadius() const { - const auto& p = mDrawingParent.promote(); + if (getDrawingState().backgroundBlurRadius == 0) { + return 0; + } + const auto& p = mDrawingParent.promote(); half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; return parentAlpha * getDrawingState().backgroundBlurRadius; } @@ -1911,7 +1854,7 @@ const std::vector<BlurRegion> Layer::getBlurRegions() const { return regionsCopy; } -Layer::RoundedCornerState Layer::getRoundedCornerState() const { +RoundedCornerState Layer::getRoundedCornerState() const { // Get parent settings RoundedCornerState parentSettings; const auto& parent = mDrawingParent.promote(); @@ -1952,39 +1895,6 @@ Layer::RoundedCornerState Layer::getRoundedCornerState() const { return {}; } -void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster, - const Rect& layerStackRect) { - renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings; - - // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if - // transparent regions are present. This may not be necessary since shadows are only cast by - // SurfaceFlinger's EffectLayers, which do not typically use transparent regions. - state.boundaries = mBounds; - - // Shift the spot light x-position to the middle of the display and then - // offset it by casting layer's screen pos. - state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left; - state.lightPos.y -= mScreenBounds.top; - - state.length = mEffectiveShadowRadius; - - if (state.length > 0.f) { - const float casterAlpha = caster.alpha; - const bool casterIsOpaque = - ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque); - - // If the casting layer is translucent, we need to fill in the shadow underneath the layer. - // Otherwise the generated shadow will only be shown around the casting layer. - state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); - state.ambientColor *= casterAlpha; - state.spotColor *= casterAlpha; - - if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) { - caster.shadow = state; - } - } -} - bool Layer::findInHierarchy(const sp<Layer>& l) { if (l == this) { return true; @@ -2012,7 +1922,7 @@ void Layer::commitChildList() { zOrderRelativeOf->mName.c_str()); ALOGE("Severing rel Z loop, potentially dangerous"); mDrawingState.isRelativeOf = false; - zOrderRelativeOf->removeZOrderRelative(this); + zOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); } } } @@ -2020,9 +1930,10 @@ void Layer::commitChildList() { void Layer::setInputInfo(const WindowInfo& info) { mDrawingState.inputInfo = info; - mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote()); + mDrawingState.touchableRegionCrop = + LayerHandle::getLayer(info.touchableRegionCropHandle.promote()); mDrawingState.modified = true; - mFlinger->mInputInfoChanged = true; + mFlinger->mUpdateInputInfo = true; setTransactionFlags(eTransactionNeeded); } @@ -2064,8 +1975,6 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo) { layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); layerInfo->set_queued_frames(getQueuedFrameCount()); layerInfo->set_curr_frame(mCurrentFrameNumber); - layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); - layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius); layerInfo->set_corner_radius( (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0); @@ -2114,7 +2023,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet } } - LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, + LayerProtoHelper::writeToProto(state.transparentRegionHint, [&]() { return layerInfo->mutable_transparent_region(); }); layerInfo->set_layer_stack(getLayerStack().id); @@ -2124,9 +2033,6 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet return layerInfo->mutable_requested_position(); }); - LayerProtoHelper::writeSizeToProto(state.width, state.height, - [&]() { return layerInfo->mutable_size(); }); - LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); }); layerInfo->set_is_opaque(isOpaque(state)); @@ -2186,14 +2092,6 @@ bool Layer::isRemovedFromCurrentState() const { return mRemovedFromDrawingState; } -ui::Transform Layer::getInputTransform() const { - return getTransform(); -} - -Rect Layer::getInputBounds() const { - return getCroppedBufferSize(getDrawingState()); -} - // Applies the given transform to the region, while protecting against overflows caused by any // offsets. If applying the offset in the transform to any of the Rects in the region would result // in an overflow, they are not added to the output Region. @@ -2292,7 +2190,7 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDi } void Layer::fillTouchOcclusionMode(WindowInfo& info) { - sp<Layer> p = this; + sp<Layer> p = sp<Layer>::fromExisting(this); while (p != nullptr && !p->hasInputInfo()) { p = p->mDrawingParent.promote(); } @@ -2432,7 +2330,7 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (isClone()) { - info.isClone = true; + info.inputConfig |= WindowInfo::InputConfig::CLONE; if (const sp<Layer> clonedRoot = getClonedRoot()) { const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds}); info.touchableRegion = info.touchableRegion.intersect(rect); @@ -2444,7 +2342,7 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { sp<Layer> Layer::getClonedRoot() { if (mClonedChild != nullptr) { - return this; + return sp<Layer>::fromExisting(this); } if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { return nullptr; @@ -2457,10 +2355,6 @@ bool Layer::hasInputInfo() const { mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); } -bool Layer::canReceiveInput() const { - return !isHiddenByPolicy(); -} - compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( const DisplayDevice* display) const { if (!display) return nullptr; @@ -2475,6 +2369,40 @@ Region Layer::getVisibleRegion(const DisplayDevice* display) const { void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { cloneDrawingState(clonedFrom.get()); mClonedFrom = clonedFrom; + mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha; + mPotentialCursor = clonedFrom->mPotentialCursor; + mProtectedByApp = clonedFrom->mProtectedByApp; + updateCloneBufferInfo(); +} + +void Layer::updateCloneBufferInfo() { + if (!isClone() || !isClonedFromAlive()) { + return; + } + + sp<Layer> clonedFrom = getClonedFrom(); + mBufferInfo = clonedFrom->mBufferInfo; + mSidebandStream = clonedFrom->mSidebandStream; + surfaceDamageRegion = clonedFrom->surfaceDamageRegion; + mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load(); + mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber; + + // After buffer info is updated, the drawingState from the real layer needs to be copied into + // the cloned. This is because some properties of drawingState can change when latchBuffer is + // called. However, copying the drawingState would also overwrite the cloned layer's relatives + // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in + // the cloned drawingState again. + wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; + SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; + wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; + WindowInfo tmpInputInfo = mDrawingState.inputInfo; + + cloneDrawingState(clonedFrom.get()); + + mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; + mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; + mDrawingState.zOrderRelatives = tmpZOrderRelatives; + mDrawingState.inputInfo = tmpInputInfo; } void Layer::updateMirrorInfo() { @@ -2498,7 +2426,7 @@ void Layer::updateMirrorInfo() { } mClonedChild->updateClonedDrawingState(clonedLayersMap); - mClonedChild->updateClonedChildren(this, clonedLayersMap); + mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap); mClonedChild->updateClonedRelatives(clonedLayersMap); } @@ -2509,7 +2437,7 @@ void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayer if (isClonedFromAlive()) { sp<Layer> clonedFrom = getClonedFrom(); cloneDrawingState(clonedFrom.get()); - clonedLayersMap.emplace(clonedFrom, this); + clonedLayersMap.emplace(clonedFrom, sp<Layer>::fromExisting(this)); } // The clone layer may have children in drawingState since they may have been created and @@ -2553,7 +2481,7 @@ void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLa if (clonedLayersMap.count(cropLayer) == 0) { // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to // self as crop layer to avoid going outside bounds. - mDrawingState.touchableRegionCrop = this; + mDrawingState.touchableRegionCrop = wp<Layer>::fromExisting(this); } else { const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer); mDrawingState.touchableRegionCrop = clonedCropLayer; @@ -2565,7 +2493,7 @@ void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLa } void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - mDrawingState.zOrderRelativeOf = nullptr; + mDrawingState.zOrderRelativeOf = wp<Layer>(); mDrawingState.zOrderRelatives.clear(); if (!isClonedFromAlive()) { @@ -2601,7 +2529,7 @@ void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLa void Layer::addChildToDrawing(const sp<Layer>& layer) { mDrawingChildren.add(layer); - layer->mDrawingParent = this; + layer->mDrawingParent = sp<Layer>::fromExisting(this); } Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) { @@ -2612,6 +2540,8 @@ Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t comp return FrameRateCompatibility::ExactOrMultiple; case ANATIVEWINDOW_FRAME_RATE_EXACT: return FrameRateCompatibility::Exact; + case ANATIVEWINDOW_FRAME_RATE_MIN: + return FrameRateCompatibility::Min; case ANATIVEWINDOW_FRAME_RATE_NO_VOTE: return FrameRateCompatibility::NoVote; default: @@ -2648,23 +2578,6 @@ void Layer::setClonedChild(const sp<Layer>& clonedChild) { mFlinger->mNumClones++; } -const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle"); - -wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) { - if (handleBinder == nullptr) { - return nullptr; - } - - BBinder* b = handleBinder->localBinder(); - if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) { - return nullptr; - } - - // We can safely cast this binder since its local and we verified its interface descriptor. - sp<Handle> handle = static_cast<Handle*>(handleBinder.get()); - return handle->owner; -} - bool Layer::setDropInputMode(gui::DropInputMode mode) { if (mDrawingState.dropInputMode == mode) { return false; @@ -2680,18 +2593,1401 @@ void Layer::cloneDrawingState(const Layer* from) { mDrawingState.callbackHandles = {}; } +void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, + const sp<GraphicBuffer>& buffer, uint64_t framenumber, + const sp<Fence>& releaseFence, + uint32_t currentMaxAcquiredBufferCount) { + if (!listener) { + return; + } + ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); + std::optional<os::ParcelFileDescriptor> fenceFd; + if (releaseFence) { + fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get()))); + } + listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd, + static_cast<int32_t>(currentMaxAcquiredBufferCount)); +} + +void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { + // If we are displayed on multiple displays in a single composition cycle then we would + // need to do careful tracking to enable the use of the mLastClientCompositionFence. + // For example we can only use it if all the displays are client comp, and we need + // to merge all the client comp fences. We could do this, but for now we just + // disable the optimization when a layer is composed on multiple displays. + if (mClearClientCompositionFenceOnLayerDisplayed) { + mLastClientCompositionFence = nullptr; + } else { + mClearClientCompositionFenceOnLayerDisplayed = true; + } + + // The previous release fence notifies the client that SurfaceFlinger is done with the previous + // buffer that was presented on this layer. The first transaction that came in this frame that + // replaced the previous buffer on this layer needs this release fence, because the fence will + // let the client know when that previous buffer is removed from the screen. + // + // Every other transaction on this layer does not need a release fence because no other + // Transactions that were set on this layer this frame are going to have their preceding buffer + // removed from the display this frame. + // + // For example, if we have 3 transactions this frame. The first transaction doesn't contain a + // buffer so it doesn't need a previous release fence because the layer still needs the previous + // buffer. The second transaction contains a buffer so it needs a previous release fence because + // the previous buffer will be released this frame. The third transaction also contains a + // buffer. It replaces the buffer in the second transaction. The buffer in the second + // transaction will now no longer be presented so it is released immediately and the third + // transaction doesn't need a previous release fence. + sp<CallbackHandle> ch; + for (auto& handle : mDrawingState.callbackHandles) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { + ch = handle; + break; + } + } + + // Prevent tracing the same release multiple times. + if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) { + mPreviousReleasedFrameNumber = mPreviousFrameNumber; + } + + if (ch != nullptr) { + ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; + ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); + ch->name = mName; + } +} + +void Layer::onSurfaceFrameCreated( + const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) { + if (!hasBufferOrSidebandStreamInDrawing()) { + return; + } + + while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) { + // Too many SurfaceFrames pending classification. The front of the deque is probably not + // tracked by FrameTimeline and will never be presented. This will only result in a memory + // leak. + ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak", + mName.c_str()); + std::string miniDump = mPendingJankClassifications.front()->miniDump(); + ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str()); + mPendingJankClassifications.pop_front(); + } + mPendingJankClassifications.emplace_back(surfaceFrame); +} + +void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { + for (const auto& handle : mDrawingState.callbackHandles) { + handle->transformHint = mSkipReportingTransformHint + ? std::nullopt + : std::make_optional<uint32_t>(mTransformHint); + handle->dequeueReadyTime = dequeueReadyTime; + handle->currentMaxAcquiredBufferCount = + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); + ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(), + handle->previousReleaseCallbackId.framenumber); + } + + for (auto& handle : mDrawingState.callbackHandles) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { + handle->previousReleaseCallbackId = mPreviousReleaseCallbackId; + break; + } + } + + std::vector<JankData> jankData; + jankData.reserve(mPendingJankClassifications.size()); + while (!mPendingJankClassifications.empty() && + mPendingJankClassifications.front()->getJankType()) { + std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame = + mPendingJankClassifications.front(); + mPendingJankClassifications.pop_front(); + jankData.emplace_back( + JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value())); + } + + mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles, + jankData); + mDrawingState.callbackHandles = {}; +} + +bool Layer::willPresentCurrentTransaction() const { + // Returns true if the most recent Transaction applied to CurrentState will be presented. + return (getSidebandStreamChanged() || getAutoRefresh() || + (mDrawingState.modified && + (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr))); +} + +bool Layer::setTransform(uint32_t transform) { + if (mDrawingState.bufferTransform == transform) return false; + mDrawingState.bufferTransform = transform; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { + if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false; + mDrawingState.sequence++; + mDrawingState.transformToDisplayInverse = transformToDisplayInverse; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setBufferCrop(const Rect& bufferCrop) { + if (mDrawingState.bufferCrop == bufferCrop) return false; + + mDrawingState.sequence++; + mDrawingState.bufferCrop = bufferCrop; + + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setDestinationFrame(const Rect& destinationFrame) { + if (mDrawingState.destinationFrame == destinationFrame) return false; + + mDrawingState.sequence++; + mDrawingState.destinationFrame = destinationFrame; + + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +// Translate destination frame into scale and position. If a destination frame is not set, use the +// provided scale and position +bool Layer::updateGeometry() { + if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) || + mDrawingState.destinationFrame.isEmpty()) { + // If destination frame is not set, use the requested transform set via + // Layer::setPosition and Layer::setMatrix. + return assignTransform(&mDrawingState.transform, mRequestedTransform); + } + + Rect destRect = mDrawingState.destinationFrame; + int32_t destW = destRect.width(); + int32_t destH = destRect.height(); + if (destRect.left < 0) { + destRect.left = 0; + destRect.right = destW; + } + if (destRect.top < 0) { + destRect.top = 0; + destRect.bottom = destH; + } + + if (!mDrawingState.buffer) { + ui::Transform t; + t.set(destRect.left, destRect.top); + return assignTransform(&mDrawingState.transform, t); + } + + uint32_t bufferWidth = mDrawingState.buffer->getWidth(); + uint32_t bufferHeight = mDrawingState.buffer->getHeight(); + // Undo any transformations on the buffer. + if (mDrawingState.bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (mDrawingState.transformToDisplayInverse) { + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + float sx = destW / static_cast<float>(bufferWidth); + float sy = destH / static_cast<float>(bufferHeight); + ui::Transform t; + t.set(sx, 0, 0, sy); + t.set(destRect.left, destRect.top); + return assignTransform(&mDrawingState.transform, t); +} + +bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { + if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && + mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { + return false; + } + + mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + + mDrawingState.sequence++; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + + return true; +} + +bool Layer::setPosition(float x, float y) { + if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) { + return false; + } + + mRequestedTransform.set(x, y); + + mDrawingState.sequence++; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + + return true; +} + +bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, + const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, + bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime, + const FrameTimelineInfo& info) { + ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); + if (!buffer) { + return false; + } + + const bool frameNumberChanged = + bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged); + const uint64_t frameNumber = + frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1; + ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber); + + if (mDrawingState.buffer) { + mReleasePreviousBuffer = true; + if (!mBufferInfo.mBuffer || + (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) || + mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) { + // If mDrawingState has a buffer, and we are about to update again + // before swapping to drawing state, then the first buffer will be + // dropped and we should decrement the pending buffer count and + // call any release buffer callbacks if set. + callReleaseBufferCallback(mDrawingState.releaseBufferListener, + mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, + mDrawingState.acquireFence, + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( + mOwnerUid)); + decrementPendingBufferCount(); + if (mDrawingState.bufferSurfaceFrameTX != nullptr && + mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) { + addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX); + mDrawingState.bufferSurfaceFrameTX.reset(); + } + } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) { + callReleaseBufferCallback(mDrawingState.releaseBufferListener, + mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, + mLastClientCompositionFence, + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( + mOwnerUid)); + mLastClientCompositionFence = nullptr; + } + } + + mDrawingState.frameNumber = frameNumber; + mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; + mDrawingState.buffer = std::move(buffer); + mDrawingState.clientCacheId = bufferData.cachedBuffer; + mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) + ? bufferData.acquireFence + : Fence::NO_FENCE; + mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence); + if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { + // We latched this buffer unsiganled, so we need to pass the acquire fence + // on the callback instead of just the acquire time, since it's unknown at + // this point. + mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence; + } else { + mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime(); + } + + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + + const int32_t layerId = getSequence(); + mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(), + mOwnerUid, postTime, getGameMode()); + mDrawingState.desiredPresentTime = desiredPresentTime; + mDrawingState.isAutoTimestamp = isAutoTimestamp; + + const nsecs_t presentTime = [&] { + if (!isAutoTimestamp) return desiredPresentTime; + + const auto prediction = + mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId); + if (prediction.has_value()) return prediction->presentTime; + + return static_cast<nsecs_t>(0); + }(); + + using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; + mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer); + + setFrameTimelineVsyncForBufferTransaction(info, postTime); + + if (dequeueTime && *dequeueTime != 0) { + const uint64_t bufferId = mDrawingState.buffer->getId(); + mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime, + FrameTracer::FrameEvent::DEQUEUE); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime, + FrameTracer::FrameEvent::QUEUE); + } + + mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; + return true; +} + +bool Layer::setDataspace(ui::Dataspace dataspace) { + mDrawingState.dataspaceRequested = true; + if (mDrawingState.dataspace == dataspace) return false; + mDrawingState.dataspace = dataspace; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) { + if (mDrawingState.hdrMetadata == hdrMetadata) return false; + mDrawingState.hdrMetadata = hdrMetadata; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) { + mDrawingState.surfaceDamageRegion = surfaceDamage; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setApi(int32_t api) { + if (mDrawingState.api == api) return false; + mDrawingState.api = api; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream) { + if (mDrawingState.sidebandStream == sidebandStream) return false; + + if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) { + mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount(); + } else if (sidebandStream != nullptr) { + mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount(); + } + + mDrawingState.sidebandStream = sidebandStream; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + if (!mSidebandStreamChanged.exchange(true)) { + // mSidebandStreamChanged was false + mFlinger->onLayerUpdate(); + } + return true; +} + bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) { + // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return if (handles.empty()) { + mReleasePreviousBuffer = false; return false; } + const bool willPresent = willPresentCurrentTransaction(); + for (const auto& handle : handles) { - mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle); + // If this transaction set a buffer on this layer, release its previous buffer + handle->releasePreviousBuffer = mReleasePreviousBuffer; + + // If this layer will be presented in this frame + if (willPresent) { + // If this transaction set an acquire fence on this layer, set its acquire time + handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; + handle->frameNumber = mDrawingState.frameNumber; + + // Store so latched time and release fence can be set + mDrawingState.callbackHandles.push_back(handle); + + } else { // If this layer will NOT need to be relatched and presented this frame + // Notify the transaction completed thread this handle is done + mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle); + } + } + + mReleasePreviousBuffer = false; + mCallbackHandleAcquireTimeOrFence = -1; + + return willPresent; +} + +Rect Layer::getBufferSize(const State& /*s*/) const { + // for buffer state layers we use the display frame size as the buffer size. + + if (mBufferInfo.mBuffer == nullptr) { + return Rect::INVALID_RECT; + } + + uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); + + // Undo any transformations on the buffer and return the result. + if (mBufferInfo.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + if (getTransformToDisplayInverse()) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + } + + return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); +} + +FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const { + if (mBufferInfo.mBuffer == nullptr) { + return parentBounds; + } + + return getBufferSize(getDrawingState()).toFloatRect(); +} + +bool Layer::fenceHasSignaled() const { + if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { + return true; } + const bool fenceSignaled = + getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; + if (!fenceSignaled) { + mFlinger->mTimeStats->incrementLatchSkipped(getSequence(), + TimeStats::LatchSkipReason::LateAcquire); + } + + return fenceSignaled; +} + +bool Layer::onPreComposition(nsecs_t refreshStartTime) { + for (const auto& handle : mDrawingState.callbackHandles) { + handle->refreshStartTime = refreshStartTime; + } + return hasReadyFrame(); +} + +void Layer::setAutoRefresh(bool autoRefresh) { + mDrawingState.autoRefresh = autoRefresh; +} + +bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) { + // We need to update the sideband stream if the layer has both a buffer and a sideband stream. + auto* snapshot = editLayerSnapshot(); + snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); + + if (mSidebandStreamChanged.exchange(false)) { + const State& s(getDrawingState()); + // mSidebandStreamChanged was true + mSidebandStream = s.sidebandStream; + snapshot->sidebandStream = mSidebandStream; + if (mSidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + recomputeVisibleRegions = true; + + return true; + } + return false; +} + +bool Layer::hasFrameUpdate() const { + const State& c(getDrawingState()); + return (mDrawingStateModified || mDrawingState.modified) && + (c.buffer != nullptr || c.bgColorLayer != nullptr); +} + +void Layer::updateTexImage(nsecs_t latchTime) { + const State& s(getDrawingState()); + + if (!s.buffer) { + if (s.bgColorLayer) { + for (auto& handle : mDrawingState.callbackHandles) { + handle->latchTime = latchTime; + } + } + return; + } + + for (auto& handle : mDrawingState.callbackHandles) { + if (handle->frameNumber == mDrawingState.frameNumber) { + handle->latchTime = latchTime; + } + } + + const int32_t layerId = getSequence(); + const uint64_t bufferId = mDrawingState.buffer->getId(); + const uint64_t frameNumber = mDrawingState.frameNumber; + const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence); + mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence); + mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime); + + mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence, + FrameTracer::FrameEvent::ACQUIRE_FENCE); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime, + FrameTracer::FrameEvent::LATCH); + + auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX; + if (bufferSurfaceFrame != nullptr && + bufferSurfaceFrame->getPresentState() != PresentState::Presented) { + // Update only if the bufferSurfaceFrame wasn't already presented. A Presented + // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we + // are processing the next state. + addSurfaceFramePresentedForBuffer(bufferSurfaceFrame, + mDrawingState.acquireFenceTime->getSignalTime(), + latchTime); + mDrawingState.bufferSurfaceFrameTX.reset(); + } + + std::deque<sp<CallbackHandle>> remainingHandles; + mFlinger->getTransactionCallbackInvoker() + .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); + mDrawingState.callbackHandles = remainingHandles; + + mDrawingStateModified = false; +} + +void Layer::gatherBufferInfo() { + if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) { + decrementPendingBufferCount(); + } + + mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber}; + mBufferInfo.mBuffer = mDrawingState.buffer; + mBufferInfo.mFence = mDrawingState.acquireFence; + mBufferInfo.mFrameNumber = mDrawingState.frameNumber; + mBufferInfo.mPixelFormat = + !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat(); + mBufferInfo.mFrameLatencyNeeded = true; + mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime; + mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); + mBufferInfo.mFence = mDrawingState.acquireFence; + mBufferInfo.mTransform = mDrawingState.bufferTransform; + auto lastDataspace = mBufferInfo.mDataspace; + mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace); + if (lastDataspace != mBufferInfo.mDataspace) { + mFlinger->mSomeDataspaceChanged = true; + } + mBufferInfo.mCrop = computeBufferCrop(mDrawingState); + mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; + mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion; + mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata; + mBufferInfo.mApi = mDrawingState.api; + mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse; +} + +Rect Layer::computeBufferCrop(const State& s) { + if (s.buffer && !s.bufferCrop.isEmpty()) { + Rect bufferCrop; + s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop); + return bufferCrop; + } else if (s.buffer) { + return s.buffer->getBounds(); + } else { + return s.bufferCrop; + } +} + +sp<Layer> Layer::createClone() { + LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); + args.textureName = mTextureName; + sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args); + layer->setInitialValuesForClone(sp<Layer>::fromExisting(this)); + return layer; +} + +bool Layer::bufferNeedsFiltering() const { + const State& s(getDrawingState()); + if (!s.buffer) { + return false; + } + + int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth()); + int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight()); + + // Undo any transformations on the buffer and return the result. + if (s.bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + + if (s.transformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + const Rect layerSize{getBounds()}; + int32_t layerWidth = layerSize.getWidth(); + int32_t layerHeight = layerSize.getHeight(); + + // Align the layer orientation with the buffer before comparism + if (mTransformHint & ui::Transform::ROT_90) { + std::swap(layerWidth, layerHeight); + } + + return layerWidth != bufferWidth || layerHeight != bufferHeight; +} + +void Layer::decrementPendingBufferCount() { + int32_t pendingBuffers = --mPendingBufferTransactions; + tracePendingBufferCount(pendingBuffers); +} + +void Layer::tracePendingBufferCount(int32_t pendingBuffers) { + ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); +} + +/* + * We don't want to send the layer's transform to input, but rather the + * parent's transform. This is because Layer's transform is + * information about how the buffer is placed on screen. The parent's + * transform makes more sense to send since it's information about how the + * layer is placed on screen. This transform is used by input to determine + * how to go from screen space back to window space. + */ +ui::Transform Layer::getInputTransform() const { + if (!hasBufferOrSidebandStream()) { + return getTransform(); + } + sp<Layer> parent = mDrawingParent.promote(); + if (parent == nullptr) { + return ui::Transform(); + } + + return parent->getTransform(); +} + +/** + * Similar to getInputTransform, we need to update the bounds to include the transform. + * This is because bounds don't include the buffer transform, where the input assumes + * that's already included. + */ +Rect Layer::getInputBounds() const { + if (!hasBufferOrSidebandStream()) { + return getCroppedBufferSize(getDrawingState()); + } + + Rect bufferBounds = getCroppedBufferSize(getDrawingState()); + if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) { + return bufferBounds; + } + return mDrawingState.transform.transform(bufferBounds); +} + +bool Layer::simpleBufferUpdate(const layer_state_t& s) const { + const uint64_t requiredFlags = layer_state_t::eBufferChanged; + + const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged | + layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged | + layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged | + layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged | + layer_state_t::eReparent; + + const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged | + layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged | + layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged | + layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged | + layer_state_t::eInputInfoChanged; + + if ((s.what & requiredFlags) != requiredFlags) { + ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__, + (s.what | requiredFlags) & ~s.what); + return false; + } + + if (s.what & deniedFlags) { + ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags); + return false; + } + + if (s.what & allowedFlags) { + ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags); + } + + if (s.what & layer_state_t::ePositionChanged) { + if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) { + ALOGV("%s: false [ePositionChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eAlphaChanged) { + if (mDrawingState.color.a != s.color.a) { + ALOGV("%s: false [eAlphaChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eColorTransformChanged) { + if (mDrawingState.colorTransform != s.colorTransform) { + ALOGV("%s: false [eColorTransformChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eBackgroundColorChanged) { + if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) { + ALOGV("%s: false [eBackgroundColorChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eMatrixChanged) { + if (mRequestedTransform.dsdx() != s.matrix.dsdx || + mRequestedTransform.dtdy() != s.matrix.dtdy || + mRequestedTransform.dtdx() != s.matrix.dtdx || + mRequestedTransform.dsdy() != s.matrix.dsdy) { + ALOGV("%s: false [eMatrixChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eCornerRadiusChanged) { + if (mDrawingState.cornerRadius != s.cornerRadius) { + ALOGV("%s: false [eCornerRadiusChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) { + if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) { + ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eBufferTransformChanged) { + if (mDrawingState.bufferTransform != s.bufferTransform) { + ALOGV("%s: false [eBufferTransformChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eTransformToDisplayInverseChanged) { + if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) { + ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eCropChanged) { + if (mDrawingState.crop != s.crop) { + ALOGV("%s: false [eCropChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eDataspaceChanged) { + if (mDrawingState.dataspace != s.dataspace) { + ALOGV("%s: false [eDataspaceChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eHdrMetadataChanged) { + if (mDrawingState.hdrMetadata != s.hdrMetadata) { + ALOGV("%s: false [eHdrMetadataChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eSidebandStreamChanged) { + if (mDrawingState.sidebandStream != s.sidebandStream) { + ALOGV("%s: false [eSidebandStreamChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eColorSpaceAgnosticChanged) { + if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) { + ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eShadowRadiusChanged) { + if (mDrawingState.shadowRadius != s.shadowRadius) { + ALOGV("%s: false [eShadowRadiusChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eFixedTransformHintChanged) { + if (mDrawingState.fixedTransformHint != s.fixedTransformHint) { + ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eTrustedOverlayChanged) { + if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) { + ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eStretchChanged) { + StretchEffect temp = s.stretchEffect; + temp.sanitize(); + if (mDrawingState.stretchEffect != temp) { + ALOGV("%s: false [eStretchChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eBufferCropChanged) { + if (mDrawingState.bufferCrop != s.bufferCrop) { + ALOGV("%s: false [eBufferCropChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eDestinationFrameChanged) { + if (mDrawingState.destinationFrame != s.destinationFrame) { + ALOGV("%s: false [eDestinationFrameChanged changed]", __func__); + return false; + } + } + + if (s.what & layer_state_t::eDimmingEnabledChanged) { + if (mDrawingState.dimmingEnabled != s.dimmingEnabled) { + ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__); + return false; + } + } + + ALOGV("%s: true", __func__); return true; } +bool Layer::isHdrY410() const { + // pixel format is HDR Y410 masquerading as RGBA_1010102 + return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ && + mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA && + mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102); +} + +sp<LayerFE> Layer::getCompositionEngineLayerFE() const { + // There's no need to get a CE Layer if the layer isn't going to draw anything. + return hasSomethingToDraw() ? mLayerFE : nullptr; +} + +const LayerSnapshot* Layer::getLayerSnapshot() const { + return mSnapshot.get(); +} + +LayerSnapshot* Layer::editLayerSnapshot() { + return mSnapshot.get(); +} + +const compositionengine::LayerFECompositionState* Layer::getCompositionState() const { + return mSnapshot.get(); +} + +sp<LayerFE> Layer::copyCompositionEngineLayerFE() const { + auto result = mFlinger->getFactory().createLayerFE(mLayerFE->getDebugName()); + result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot); + return result; +} + +void Layer::useSurfaceDamage() { + if (mFlinger->mForceFullDamage) { + surfaceDamageRegion = Region::INVALID_REGION; + } else { + surfaceDamageRegion = mBufferInfo.mSurfaceDamage; + } +} + +void Layer::useEmptyDamage() { + surfaceDamageRegion.clear(); +} + +bool Layer::isOpaque(const Layer::State& s) const { + // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the + // layer's opaque flag. + if (!hasSomethingToDraw()) { + return false; + } + + // if the layer has the opaque flag, then we're always opaque + if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) { + return true; + } + + // If the buffer has no alpha channel, then we are opaque + if (hasBufferOrSidebandStream() && isOpaqueFormat(getPixelFormat())) { + return true; + } + + // Lastly consider the layer opaque if drawing a color with alpha == 1.0 + return fillsColor() && getAlpha() == 1.0_hf; +} + +bool Layer::canReceiveInput() const { + return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f); +} + +bool Layer::isVisible() const { + if (!hasSomethingToDraw()) { + return false; + } + + if (isHiddenByPolicy()) { + return false; + } + + return getAlpha() > 0.0f || hasBlur(); +} + +void Layer::onPostComposition(const DisplayDevice* display, + const std::shared_ptr<FenceTime>& glDoneFence, + const std::shared_ptr<FenceTime>& presentFence, + const CompositorTiming& compositorTiming) { + // mFrameLatencyNeeded is true when a new frame was latched for the + // composition. + if (!mBufferInfo.mFrameLatencyNeeded) return; + + for (const auto& handle : mDrawingState.callbackHandles) { + handle->gpuCompositionDoneFence = glDoneFence; + handle->compositorTiming = compositorTiming; + } + + // Update mFrameTracker. + nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; + mFrameTracker.setDesiredPresentTime(desiredPresentTime); + + const int32_t layerId = getSequence(); + mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime); + + const auto outputLayer = findOutputLayerForDisplay(display); + if (outputLayer && outputLayer->requiresClientComposition()) { + nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp; + mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, + clientCompositionTimestamp, + FrameTracer::FrameEvent::FALLBACK_COMPOSITION); + // Update the SurfaceFrames in the drawing state + if (mDrawingState.bufferSurfaceFrameTX) { + mDrawingState.bufferSurfaceFrameTX->setGpuComposition(); + } + for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { + surfaceFrame->setGpuComposition(); + } + } + + std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; + if (frameReadyFence->isValid()) { + mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); + } else { + // There was no fence for this frame, so assume that it was ready + // to be presented at the desired present time. + mFrameTracker.setFrameReadyTime(desiredPresentTime); + } + + if (display) { + const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps; + const std::optional<Fps> renderRate = + mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); + + const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate); + const auto gameMode = getGameMode(); + + if (presentFence->isValid()) { + mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, + refreshRate, renderRate, vote, gameMode); + mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, + presentFence, + FrameTracer::FrameEvent::PRESENT_FENCE); + mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); + } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); + displayId && mFlinger->getHwComposer().isConnected(*displayId)) { + // The HWC doesn't support present fences, so use the present timestamp instead. + const nsecs_t presentTimestamp = + mFlinger->getHwComposer().getPresentTimestamp(*displayId); + + const nsecs_t now = systemTime(CLOCK_MONOTONIC); + const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC(); + const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod); + + mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, + refreshRate, renderRate, vote, gameMode); + mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), + mCurrentFrameNumber, actualPresentTime, + FrameTracer::FrameEvent::PRESENT_FENCE); + mFrameTracker.setActualPresentTime(actualPresentTime); + } + } + + mFrameTracker.advanceFrame(); + mBufferInfo.mFrameLatencyNeeded = false; +} + +bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { + ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), + getDrawingState().frameNumber); + + bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); + + if (refreshRequired) { + return refreshRequired; + } + + // If the head buffer's acquire fence hasn't signaled yet, return and + // try again later + if (!fenceHasSignaled()) { + ATRACE_NAME("!fenceHasSignaled()"); + mFlinger->onLayerUpdate(); + return false; + } + + updateTexImage(latchTime); + if (mDrawingState.buffer == nullptr) { + return false; + } + + // Capture the old state of the layer for comparisons later + BufferInfo oldBufferInfo = mBufferInfo; + const bool oldOpacity = isOpaque(mDrawingState); + mPreviousFrameNumber = mCurrentFrameNumber; + mCurrentFrameNumber = mDrawingState.frameNumber; + gatherBufferInfo(); + + if (oldBufferInfo.mBuffer == nullptr) { + // the first time we receive a buffer, we need to trigger a + // geometry invalidation. + recomputeVisibleRegions = true; + } + + if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) || + (mBufferInfo.mTransform != oldBufferInfo.mTransform) || + (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) || + (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) { + recomputeVisibleRegions = true; + } + + if (oldBufferInfo.mBuffer != nullptr) { + uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); + if (bufWidth != oldBufferInfo.mBuffer->getWidth() || + bufHeight != oldBufferInfo.mBuffer->getHeight()) { + recomputeVisibleRegions = true; + } + } + + if (oldOpacity != isOpaque(mDrawingState)) { + recomputeVisibleRegions = true; + } + + return true; +} + +bool Layer::hasReadyFrame() const { + return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh(); +} + +bool Layer::isProtected() const { + return (mBufferInfo.mBuffer != nullptr) && + (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); +} + +// As documented in libhardware header, formats in the range +// 0x100 - 0x1FF are specific to the HAL implementation, and +// are known to have no alpha channel +// TODO: move definition for device-specific range into +// hardware.h, instead of using hard-coded values here. +#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) + +bool Layer::isOpaqueFormat(PixelFormat format) { + if (HARDWARE_IS_DEVICE_FORMAT(format)) { + return true; + } + switch (format) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_R_8: + return false; + } + // in all other case, we have no blending (also for unknown formats) + return true; +} + +bool Layer::needsFiltering(const DisplayDevice* display) const { + if (!hasBufferOrSidebandStream()) { + return false; + } + const auto outputLayer = findOutputLayerForDisplay(display); + if (outputLayer == nullptr) { + return false; + } + + // We need filtering if the sourceCrop rectangle size does not match the + // displayframe rectangle size (not a 1:1 render) + const auto& compositionState = outputLayer->getState(); + const auto displayFrame = compositionState.displayFrame; + const auto sourceCrop = compositionState.sourceCrop; + return sourceCrop.getHeight() != displayFrame.getHeight() || + sourceCrop.getWidth() != displayFrame.getWidth(); +} + +bool Layer::needsFilteringForScreenshots(const DisplayDevice* display, + const ui::Transform& inverseParentTransform) const { + if (!hasBufferOrSidebandStream()) { + return false; + } + const auto outputLayer = findOutputLayerForDisplay(display); + if (outputLayer == nullptr) { + return false; + } + + // We need filtering if the sourceCrop rectangle size does not match the + // viewport rectangle size (not a 1:1 render) + const auto& compositionState = outputLayer->getState(); + const ui::Transform& displayTransform = display->getTransform(); + const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse(); + // Undo the transformation of the displayFrame so that we're back into + // layer-stack space. + const Rect frame = inverseTransform.transform(compositionState.displayFrame); + const FloatRect sourceCrop = compositionState.sourceCrop; + + int32_t frameHeight = frame.getHeight(); + int32_t frameWidth = frame.getWidth(); + // If the display transform had a rotational component then undo the + // rotation so that the orientation matches the source crop. + if (displayTransform.getOrientation() & ui::Transform::ROT_90) { + std::swap(frameHeight, frameWidth); + } + return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth; +} + +void Layer::latchAndReleaseBuffer() { + if (hasReadyFrame()) { + bool ignored = false; + latchBuffer(ignored, systemTime()); + } + releasePendingBuffer(systemTime()); +} + +PixelFormat Layer::getPixelFormat() const { + return mBufferInfo.mPixelFormat; +} + +bool Layer::getTransformToDisplayInverse() const { + return mBufferInfo.mTransformToDisplayInverse; +} + +Rect Layer::getBufferCrop() const { + // this is the crop rectangle that applies to the buffer + // itself (as opposed to the window) + if (!mBufferInfo.mCrop.isEmpty()) { + // if the buffer crop is defined, we use that + return mBufferInfo.mCrop; + } else if (mBufferInfo.mBuffer != nullptr) { + // otherwise we use the whole buffer + return mBufferInfo.mBuffer->getBounds(); + } else { + // if we don't have a buffer yet, we use an empty/invalid crop + return Rect(); + } +} + +uint32_t Layer::getBufferTransform() const { + return mBufferInfo.mTransform; +} + +ui::Dataspace Layer::getDataSpace() const { + return mDrawingState.dataspaceRequested ? getRequestedDataSpace() : ui::Dataspace::UNKNOWN; +} + +ui::Dataspace Layer::getRequestedDataSpace() const { + return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace; +} + +ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { + ui::Dataspace updatedDataspace = dataspace; + // translate legacy dataspaces to modern dataspaces + switch (dataspace) { + case ui::Dataspace::SRGB: + updatedDataspace = ui::Dataspace::V0_SRGB; + break; + case ui::Dataspace::SRGB_LINEAR: + updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR; + break; + case ui::Dataspace::JFIF: + updatedDataspace = ui::Dataspace::V0_JFIF; + break; + case ui::Dataspace::BT601_625: + updatedDataspace = ui::Dataspace::V0_BT601_625; + break; + case ui::Dataspace::BT601_525: + updatedDataspace = ui::Dataspace::V0_BT601_525; + break; + case ui::Dataspace::BT709: + updatedDataspace = ui::Dataspace::V0_BT709; + break; + default: + break; + } + + return updatedDataspace; +} + +sp<GraphicBuffer> Layer::getBuffer() const { + return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr; +} + +void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) { + mTransformHint = getFixedTransformHint(); + if (mTransformHint == ui::Transform::ROT_INVALID) { + mTransformHint = displayTransformHint; + } + mSkipReportingTransformHint = false; +} + +const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const { + return mBufferInfo.mBuffer; +} + +bool Layer::setColor(const half3& color) { + if (mDrawingState.color.rgb == color) { + return false; + } + + mDrawingState.sequence++; + mDrawingState.color.rgb = color; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::fillsColor() const { + return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf && + mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf; +} + +bool Layer::hasBlur() const { + return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0; +} + +void Layer::updateSnapshot(bool updateGeometry) { + if (!getCompositionEngineLayerFE()) { + return; + } + + auto* snapshot = editLayerSnapshot(); + if (updateGeometry) { + prepareBasicGeometryCompositionState(); + prepareGeometryCompositionState(); + snapshot->roundedCorner = getRoundedCornerState(); + snapshot->stretchEffect = getStretchEffect(); + snapshot->transformedBounds = mScreenBounds; + if (mEffectiveShadowRadius > 0.f) { + snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings; + + // Note: this preserves existing behavior of shadowing the entire layer and not cropping + // it if transparent regions are present. This may not be necessary since shadows are + // typically cast by layers without transparent regions. + snapshot->shadowSettings.boundaries = mBounds; + + const float casterAlpha = snapshot->alpha; + const bool casterIsOpaque = + ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState)); + + // If the casting layer is translucent, we need to fill in the shadow underneath the + // layer. Otherwise the generated shadow will only be shown around the casting layer. + snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); + snapshot->shadowSettings.ambientColor *= casterAlpha; + snapshot->shadowSettings.spotColor *= casterAlpha; + } + snapshot->shadowSettings.length = mEffectiveShadowRadius; + } + snapshot->contentOpaque = isOpaque(mDrawingState); + snapshot->isHdrY410 = isHdrY410(); + snapshot->bufferNeedsFiltering = bufferNeedsFiltering(); + sp<Layer> p = mDrawingParent.promote(); + if (p != nullptr) { + snapshot->transform = p->getTransform(); + } else { + snapshot->transform.reset(); + } + snapshot->bufferSize = getBufferSize(mDrawingState); + snapshot->externalTexture = mBufferInfo.mBuffer; + snapshot->hasReadyFrame = hasReadyFrame(); + preparePerFrameCompositionState(); +} + +void Layer::updateChildrenSnapshots(bool updateGeometry) { + for (const sp<Layer>& child : mDrawingChildren) { + child->updateSnapshot(updateGeometry); + child->updateChildrenSnapshots(updateGeometry); + } +} + +void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) { + mSnapshot->layerMetadata = parentMetadata; + mSnapshot->layerMetadata.merge(mDrawingState.metadata); + for (const sp<Layer>& child : mDrawingChildren) { + child->updateMetadataSnapshot(mSnapshot->layerMetadata); + } +} + +void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata, + std::unordered_set<Layer*>& visited) { + if (visited.find(this) != visited.end()) { + ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName()); + return; + } + visited.insert(this); + + mSnapshot->relativeLayerMetadata = relativeLayerMetadata; + + if (mDrawingState.zOrderRelatives.empty()) { + return; + } + LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata; + childRelativeLayerMetadata.merge(mSnapshot->layerMetadata); + for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) { + sp<Layer> relative = weakRelative.promote(); + if (!relative) { + continue; + } + relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited); + } +} + +LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) { + if (mLayer) { + mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot); + } +} + +LayerSnapshotGuard::~LayerSnapshotGuard() { + if (mLayer) { + mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot); + } +} + +LayerSnapshotGuard::LayerSnapshotGuard(LayerSnapshotGuard&& other) : mLayer(other.mLayer) { + other.mLayer = nullptr; +} + +LayerSnapshotGuard& LayerSnapshotGuard::operator=(LayerSnapshotGuard&& other) { + mLayer = other.mLayer; + other.mLayer = nullptr; + return *this; +} + // --------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 200baf0ba1..08a13a34eb 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -17,8 +17,8 @@ #pragma once #include <android/gui/DropInputMode.h> +#include <android/gui/ISurfaceComposerClient.h> #include <gui/BufferQueue.h> -#include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> #include <gui/WindowInfo.h> #include <layerproto/LayerProtoHeader.h> @@ -38,6 +38,7 @@ #include <utils/Timers.h> #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <scheduler/Fps.h> #include <scheduler/Seamlessness.h> @@ -48,13 +49,10 @@ #include <vector> #include "Client.h" -#include "ClientCache.h" -#include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/HWComposer.h" #include "FrameTracker.h" +#include "LayerFE.h" #include "LayerVector.h" -#include "MonitoredProducer.h" -#include "RenderArea.h" #include "Scheduler/LayerInfo.h" #include "SurfaceFlinger.h" #include "Tracing/LayerTracing.h" @@ -69,39 +67,22 @@ class Colorizer; class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; -class LayerDebugInfo; namespace compositionengine { class OutputLayer; struct LayerFECompositionState; } -namespace impl { -class SurfaceInterceptor; +namespace gui { +class LayerDebugInfo; } namespace frametimeline { class SurfaceFrame; } // namespace frametimeline -struct LayerCreationArgs { - LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata); - - SurfaceFlinger* flinger; - const sp<Client> client; - std::string name; - uint32_t flags; - LayerMetadata metadata; - - pid_t callingPid; - uid_t callingUid; - uint32_t textureName; - std::optional<uint32_t> sequence = std::nullopt; - bool addToRoot = true; -}; - -class Layer : public virtual RefBase, compositionengine::LayerFE { - static std::atomic<int32_t> sSequence; +class Layer : public virtual RefBase { +public: // The following constants represent priority of the window. SF uses this information when // deciding which window has a priority when deciding about the refresh rate of the screen. // Priority 0 is considered the highest priority. -1 means that the priority is unset. @@ -113,7 +94,6 @@ class Layer : public virtual RefBase, compositionengine::LayerFE { // Windows that are not in focus, but voted for a specific mode ID. static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; -public: enum { // flags for doTransaction() eDontUpdateGeometryState = 0x00000001, eVisibleRegion = 0x00000002, @@ -132,73 +112,38 @@ public: inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); } }; - struct RoundedCornerState { - RoundedCornerState() = default; - RoundedCornerState(const FloatRect& cropRect, const vec2& radius) - : cropRect(cropRect), radius(radius) {} - - // Rounded rectangle in local layer coordinate space. - FloatRect cropRect = FloatRect(); - // Radius of the rounded rectangle. - vec2 radius; - bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; } - }; - using FrameRate = scheduler::LayerInfo::FrameRate; using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility; struct State { - Geometry active_legacy; - Geometry requested_legacy; int32_t z; - ui::LayerStack layerStack; - uint32_t flags; - uint8_t reserved[2]; int32_t sequence; // changes when visible regions can change bool modified; - // Crop is expressed in layer space coordinate. Rect crop; - Rect requestedCrop; - - // the transparentRegion hint is a bit special, it's latched only - // when we receive a buffer -- this is because it's "content" - // dependent. - Region activeTransparentRegion_legacy; - Region requestedTransparentRegion_legacy; - LayerMetadata metadata; - // If non-null, a Surface this Surface's Z-order is interpreted relative to. wp<Layer> zOrderRelativeOf; bool isRelativeOf{false}; // A list of surfaces whose Z-order is interpreted relative to ours. SortedVector<wp<Layer>> zOrderRelatives; - half4 color; float cornerRadius; int backgroundBlurRadius; - gui::WindowInfo inputInfo; wp<Layer> touchableRegionCrop; - // dataspace is only used by BufferStateLayer and EffectLayer ui::Dataspace dataspace; + bool dataspaceRequested; - // The fields below this point are only used by BufferStateLayer uint64_t frameNumber; - uint32_t width; - uint32_t height; ui::Transform transform; - uint32_t bufferTransform; bool transformToDisplayInverse; - Region transparentRegionHint; - std::shared_ptr<renderengine::ExternalTexture> buffer; client_cache_t clientCacheId; sp<Fence> acquireFence; @@ -206,11 +151,9 @@ public: HdrMetadata hdrMetadata; Region surfaceDamageRegion; int32_t api; - sp<NativeHandle> sidebandStream; mat4 colorTransform; bool hasColorTransform; - // pointer to background color layer that, if set, appears below the buffer state layer // and the buffer state layer's children. Z order will be set to // INT_MIN @@ -233,6 +176,8 @@ public: // Priority of the layer assigned by Window Manager. int32_t frameRateSelectionPriority; + // Default frame rate compatibility used to set the layer refresh rate votetype. + FrameRateCompatibility defaultFrameRateCompatibility; FrameRate frameRate; // The combined frame rate of parents / children of this layer @@ -252,7 +197,6 @@ public: // When the transaction was posted nsecs_t postTime; - sp<ITransactionCompletedListener> releaseBufferListener; // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync. @@ -273,59 +217,14 @@ public: // Whether or not this layer is a trusted overlay for input bool isTrustedOverlay; - Rect bufferCrop; Rect destinationFrame; - sp<IBinder> releaseBufferEndpoint; - gui::DropInputMode dropInputMode; - bool autoRefresh = false; - bool dimmingEnabled = true; }; - /* - * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer) - * is called. - */ - class LayerCleaner { - sp<SurfaceFlinger> mFlinger; - sp<Layer> mLayer; - BBinder* mHandle; - - protected: - ~LayerCleaner() { - // destroy client resources - mFlinger->onHandleDestroyed(mHandle, mLayer); - } - - public: - LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle) - : mFlinger(flinger), mLayer(layer), mHandle(handle) {} - }; - - /* - * The layer handle is just a BBinder object passed to the client - * (remote process) -- we don't keep any reference on our side such that - * the dtor is called when the remote side let go of its reference. - * - * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for - * this layer when the handle is destroyed. - */ - class Handle : public BBinder, public LayerCleaner { - public: - Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : LayerCleaner(flinger, layer, this), owner(layer) {} - const String16& getInterfaceDescriptor() const override { return kDescriptor; } - - static const String16 kDescriptor; - wp<Layer> owner; - }; - - static wp<Layer> fromHandle(const sp<IBinder>& handle); - explicit Layer(const LayerCreationArgs& args); virtual ~Layer(); @@ -333,43 +232,17 @@ public: static void miniDumpHeader(std::string& result); // Provide unique string for each class type in the Layer hierarchy - virtual const char* getType() const = 0; + virtual const char* getType() const { return "Layer"; } // true if this layer is visible, false otherwise - virtual bool isVisible() const = 0; + virtual bool isVisible() const; - virtual sp<Layer> createClone() = 0; + virtual sp<Layer> createClone(); - // Geometry setting functions. - // - // The following group of functions are used to specify the layers - // bounds, and the mapping of the texture on to those bounds. According - // to various settings changes to them may apply immediately, or be delayed until - // a pending resize is completed by the producer submitting a buffer. For example - // if we were to change the buffer size, and update the matrix ahead of the - // new buffer arriving, then we would be stretching the buffer to a different - // aspect before and after the buffer arriving, which probably isn't what we wanted. - // - // The first set of geometry functions are controlled by the scaling mode, described - // in window.h. The scaling mode may be set by the client, as it submits buffers. - // - // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then - // matrix updates will not be applied while a resize is pending - // and the size and transform will remain in their previous state - // until a new buffer is submitted. If the scaling mode is another value - // then the old-buffer will immediately be scaled to the pending size - // and the new matrix will be immediately applied following this scaling - // transformation. - - // Set the default buffer size for the assosciated Producer, in pixels. This is - // also the rendered size of the layer prior to any transformations. Parent - // or local matrix transformations will not affect the size of the buffer, - // but may affect it's on-screen size or clipping. - virtual bool setSize(uint32_t w, uint32_t h); // Set a 2x2 transformation matrix on the layer. This transform // will be applied after parent transforms, but before any final // producer specified transform. - virtual bool setMatrix(const layer_state_t::matrix22_t& matrix); + bool setMatrix(const layer_state_t::matrix22_t& matrix); // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be @@ -379,9 +252,9 @@ public: // setPosition operates in parent buffer space (pre parent-transform) or display // space for top-level layers. - virtual bool setPosition(float x, float y); + bool setPosition(float x, float y); // Buffer space - virtual bool setCrop(const Rect& crop); + bool setCrop(const Rect& crop); // TODO(b/38182121): Could we eliminate the various latching modes by // using the layer hierarchy? @@ -390,7 +263,7 @@ public: virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ); virtual bool setAlpha(float alpha); - virtual bool setColor(const half3& /*color*/) { return false; }; + bool setColor(const half3& /*color*/); // Set rounded corner radius for this layer and its children. // @@ -402,11 +275,13 @@ public: // is specified in pixels. virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions); - virtual bool setTransparentRegionHint(const Region& transparent); + bool setTransparentRegionHint(const Region& transparent); virtual bool setTrustedOverlay(bool); virtual bool setFlags(uint32_t flags, uint32_t mask); virtual bool setLayerStack(ui::LayerStack); - virtual ui::LayerStack getLayerStack() const; + virtual ui::LayerStack getLayerStack( + LayerVector::StateSet state = LayerVector::StateSet::Drawing) const; + virtual bool setMetadata(const LayerMetadata& data); virtual void setChildrenDrawingParent(const sp<Layer>&); virtual bool reparent(const sp<IBinder>& newParentHandle); @@ -416,44 +291,47 @@ public: virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; } virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }; - // Used only to set BufferStateLayer state - virtual bool setTransform(uint32_t /*transform*/) { return false; }; - virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; }; - virtual bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, - const BufferData& /* bufferData */, nsecs_t /* postTime */, - nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - std::optional<nsecs_t> /* dequeueTime */, - const FrameTimelineInfo& /*info*/) { - return false; - }; - virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; }; - virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; }; - virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; }; - virtual bool setApi(int32_t /*api*/) { return false; }; - virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; }; - virtual bool setTransactionCompletedListeners( - const std::vector<sp<CallbackHandle>>& /*handles*/); + bool setTransform(uint32_t /*transform*/); + bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/); + bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, + const BufferData& /* bufferData */, nsecs_t /* postTime */, + nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, + std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/); + bool setDataspace(ui::Dataspace /*dataspace*/); + bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); + bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); + bool setApi(int32_t /*api*/); + bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/); + bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/); virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace); virtual bool setColorSpaceAgnostic(const bool agnostic); virtual bool setDimmingEnabled(const bool dimmingEnabled); + virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility); virtual bool setFrameRateSelectionPriority(int32_t priority); virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint); - virtual void setAutoRefresh(bool /* autoRefresh */) {} + void setAutoRefresh(bool /* autoRefresh */); bool setDropInputMode(gui::DropInputMode); // If the variable is not set on the layer, it traverses up the tree to inherit the frame // rate priority from its parent. virtual int32_t getFrameRateSelectionPriority() const; - virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; } + // + virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const; + // + ui::Dataspace getDataSpace() const; + ui::Dataspace getRequestedDataSpace() const; + + virtual sp<LayerFE> getCompositionEngineLayerFE() const; + virtual sp<LayerFE> copyCompositionEngineLayerFE() const; - virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const; - virtual compositionengine::LayerFECompositionState* editCompositionState(); + const LayerSnapshot* getLayerSnapshot() const; + LayerSnapshot* editLayerSnapshot(); // If we have received a new buffer this frame, we will pass its surface // damage down to hardware composer. Otherwise, we must send a region with // one empty rect. - virtual void useSurfaceDamage() {} - virtual void useEmptyDamage() {} + void useSurfaceDamage(); + void useEmptyDamage(); Region getVisibleRegion(const DisplayDevice*) const; /* @@ -463,18 +341,18 @@ public: * pixel format includes an alpha channel) and the "opaque" flag set * on the layer. It does not examine the current plane alpha value. */ - virtual bool isOpaque(const Layer::State&) const { return false; } + bool isOpaque(const Layer::State&) const; /* * Returns whether this layer can receive input. */ - virtual bool canReceiveInput() const; + bool canReceiveInput() const; /* * isProtected - true if the layer may contain protected contents in the * GRALLOC_USAGE_PROTECTED sense. */ - virtual bool isProtected() const { return false; } + bool isProtected() const; /* * isFixedSize - true if content has a fixed size @@ -484,21 +362,19 @@ public: /* * usesSourceCrop - true if content should use a source crop */ - virtual bool usesSourceCrop() const { return false; } + bool usesSourceCrop() const { return hasBufferOrSidebandStream(); } // 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 virtual bool isCreatedFromMainThread() const { return false; } - uint32_t getActiveWidth(const Layer::State& s) const { return s.width; } - uint32_t getActiveHeight(const Layer::State& s) const { return s.height; } ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; } - virtual Region getActiveTransparentRegion(const Layer::State& s) const { - return s.activeTransparentRegion_legacy; + Region getActiveTransparentRegion(const Layer::State& s) const { + return s.transparentRegionHint; } - virtual Rect getCrop(const Layer::State& s) const { return s.crop; } - virtual bool needsFiltering(const DisplayDevice*) const { return false; } + Rect getCrop(const Layer::State& s) const { return s.crop; } + bool needsFiltering(const DisplayDevice*) const; // True if this layer requires filtering // This method is distinct from needsFiltering() in how the filter @@ -509,32 +385,25 @@ public: // different. // If the parent transform needs to be undone when capturing the layer, then // the inverse parent transform is also required. - virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const { - return false; - } - - virtual void updateCloneBufferInfo(){}; + bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const; - virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} + // from graphics API + ui::Dataspace translateDataspace(ui::Dataspace dataspace); + void updateCloneBufferInfo(); + uint64_t mPreviousFrameNumber = 0; - virtual bool isHdrY410() const { return false; } - - virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } + bool isHdrY410() const; /* * called after composition. * returns true if the layer latched a new buffer this frame. */ - virtual void onPostComposition(const DisplayDevice*, - const std::shared_ptr<FenceTime>& /*glDoneFence*/, - const std::shared_ptr<FenceTime>& /*presentFence*/, - const CompositorTiming&) {} + void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/, + const std::shared_ptr<FenceTime>& /*presentFence*/, + const CompositorTiming&); // If a buffer was replaced this frame, release the former buffer - virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { } - - virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/, - const CompositorTiming& /*compositorTiming*/) {} + void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/); /* * latchBuffer - called each time the screen is redrawn and returns whether @@ -542,89 +411,110 @@ 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*/, - nsecs_t /*expectedPresentTime*/) { - return false; - } + bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/); - virtual void latchAndReleaseBuffer() {} + /* + * Calls latchBuffer if the buffer has a frame queued and then releases the buffer. + * This is used if the buffer is just latched and releases to free up the buffer + * and will not be shown on screen. + * Should only be called on the main thread. + */ + void latchAndReleaseBuffer(); /* * returns the rectangle that crops the content of the layer and scales it * to the layer's size. */ - virtual Rect getBufferCrop() const { return Rect(); } + Rect getBufferCrop() const; /* * Returns the transform applied to the buffer. */ - virtual uint32_t getBufferTransform() const { return 0; } + uint32_t getBufferTransform() const; - virtual sp<GraphicBuffer> getBuffer() const { return nullptr; } - virtual const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const { - return mDrawingState.buffer; - }; + sp<GraphicBuffer> getBuffer() const; + const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const; - virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; } + ui::Transform::RotationFlags getTransformHint() const { return mTransformHint; } /* * Returns if a frame is ready */ - virtual bool hasReadyFrame() const { return false; } + bool hasReadyFrame() const; virtual int32_t getQueuedFrameCount() const { return 0; } /** * Returns active buffer size in the correct orientation. Buffer size is determined by undoing - * any buffer transformations. If the layer has no buffer then return INVALID_RECT. + * any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the + * layer does not have a display frame and its parent is not bounded. */ - virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; } + Rect getBufferSize(const Layer::State&) const; /** * Returns the source bounds. If the bounds are not defined, it is inferred from the * buffer size. Failing that, the bounds are determined from the passed in parent bounds. * For the root layer, this is the display viewport size. */ - virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const { - return parentBounds; - } + FloatRect computeSourceBounds(const FloatRect& parentBounds) const; virtual FrameRate getFrameRateForLayerTree() const; - virtual bool getTransformToDisplayInverse() const { return false; } + bool getTransformToDisplayInverse() const; // Returns how rounded corners should be drawn for this layer. // A layer can override its parent's rounded corner settings if the parent's rounded // corner crop does not intersect with its own rounded corner crop. virtual RoundedCornerState getRoundedCornerState() const; - bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); } + bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); } - virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; } + PixelFormat getPixelFormat() const; /** - * Return whether this layer needs an input info. For most layer types - * this is only true if they explicitly set an input-info but BufferLayer - * overrides this so we can generate input-info for Buffered layers that don't - * have them (for input occlusion detection checks). + * Return whether this layer needs an input info. We generate InputWindowHandles for all + * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable + * the InputDispatcher to do PID based occlusion detection. */ - virtual bool needsInputInfo() const { return hasInputInfo(); } + bool needsInputInfo() const { + return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor; + } // Implements RefBase. void onFirstRef() override; + struct BufferInfo { + nsecs_t mDesiredPresentTime; + std::shared_ptr<FenceTime> mFenceTime; + sp<Fence> mFence; + uint32_t mTransform{0}; + ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN}; + Rect mCrop; + uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; + Region mSurfaceDamage; + HdrMetadata mHdrMetadata; + int mApi; + PixelFormat mPixelFormat{PIXEL_FORMAT_NONE}; + bool mTransformToDisplayInverse{false}; + + std::shared_ptr<renderengine::ExternalTexture> mBuffer; + uint64_t mFrameNumber; + + bool mFrameLatencyNeeded{false}; + }; + + BufferInfo mBufferInfo; + // implements compositionengine::LayerFE - const compositionengine::LayerFECompositionState* getCompositionState() const override; - bool onPreComposition(nsecs_t) override; - void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override; - std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList( - compositionengine::LayerFE::ClientCompositionTargetSettings&) override; - void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; - - void setWasClientComposed(const sp<Fence>& fence) override { + const compositionengine::LayerFECompositionState* getCompositionState() const; + bool fenceHasSignaled() const; + bool onPreComposition(nsecs_t refreshStartTime); + void onLayerDisplayed(ftl::SharedFuture<FenceResult>); + + void setWasClientComposed(const sp<Fence>& fence) { mLastClientCompositionFence = fence; mClearClientCompositionFenceOnLayerDisplayed = false; } - const char* getDebugName() const override; + const char* getDebugName() const; bool setShadowRadius(float shadowRadius); @@ -649,7 +539,7 @@ public: // Compute bounds for the layer and cache the results. void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); - int32_t getSequence() const override { return sequence; } + int32_t getSequence() const { return sequence; } // For tracing. // TODO: Replace with raw buffer id from buffer metadata when that becomes available. @@ -732,15 +622,15 @@ public: * Sets display transform hint on BufferLayerConsumer. */ void updateTransformHint(ui::Transform::RotationFlags); - + void skipReportingTransformHint(); inline const State& getDrawingState() const { return mDrawingState; } inline State& getDrawingState() { return mDrawingState; } - LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const; + gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const; void miniDump(std::string& result, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; - void dumpCallingUidPid(std::string& result) const; + void dumpOffscreenDebugInfo(std::string& result) const; void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; @@ -886,22 +776,41 @@ public: bool mPendingHWCDestroy{false}; - bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; } + bool backpressureEnabled() const { + return mDrawingState.flags & layer_state_t::eEnableBackpressure; + } bool setStretchEffect(const StretchEffect& effect); StretchEffect getStretchEffect() const; - - virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; } - virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; } - virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; } - virtual std::string getPendingBufferCounterName() { return ""; } - virtual bool updateGeometry() { return false; } - - virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; } + bool enableBorder(bool shouldEnable, float width, const half4& color); + bool isBorderEnabled(); + float getBorderWidth(); + const half4& getBorderColor(); + + bool setBufferCrop(const Rect& /* bufferCrop */); + bool setDestinationFrame(const Rect& /* destinationFrame */); + // See mPendingBufferTransactions + void decrementPendingBufferCount(); + std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; } + std::string getPendingBufferCounterName() { return mBlastTransactionName; } + bool updateGeometry(); + + bool simpleBufferUpdate(const layer_state_t&) const; + + static bool isOpaqueFormat(PixelFormat format); + + // Updates the LayerSnapshot. This must be called prior to sending layer data to + // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or + // LayerFE::prepareClientComposition). + // + // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through + // CompositionEngine to create a single path for composing layers. + void updateSnapshot(bool updateGeometry); + void updateMetadataSnapshot(const LayerMetadata& parentMetadata); + void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata, + std::unordered_set<Layer*>& visited); protected: - friend class impl::SurfaceInterceptor; - // For unit tests friend class TestableSurfaceFlinger; friend class FpsReporterTest; @@ -911,18 +820,13 @@ protected: friend class TransactionSurfaceFrameTest; virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); - virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings&); - virtual void preparePerFrameCompositionState(); + void preparePerFrameCompositionState(); + void preparePerFrameBufferCompositionState(); + void preparePerFrameEffectsCompositionState(); virtual void commitTransaction(State& stateToCommit); - virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {} - - // Returns mCurrentScaling mode (originating from the - // Client) or mOverrideScalingMode mode (originating from - // the Surface Controller) if set. - virtual uint32_t getEffectiveScalingMode() const { return 0; } + void gatherBufferInfo(); + void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); - sp<compositionengine::LayerFE> asLayerFE() const; sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; } bool isClone() { return mClonedFrom != nullptr; } bool isClonedFromAlive() { return getClonedFrom() != nullptr; } @@ -935,11 +839,6 @@ protected: void addChildToDrawing(const sp<Layer>&); void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - // Modifies the passed in layer settings to clear the contents. If the blackout flag is set, - // the settings clears the content with a solid black fill. - void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const; - void prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect); - void prepareBasicGeometryCompositionState(); void prepareGeometryCompositionState(); void prepareCursorCompositionState(); @@ -972,7 +871,7 @@ protected: * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input * in this layer's space, regardless of the specified crop layer. */ - virtual Rect getInputBounds() const; + Rect getInputBounds() const; // constant sp<SurfaceFlinger> mFlinger; @@ -1043,7 +942,14 @@ protected: sp<Fence> mLastClientCompositionFence; bool mClearClientCompositionFenceOnLayerDisplayed = false; private: - virtual void setTransformHint(ui::Transform::RotationFlags) {} + friend class SlotGenerationTest; + friend class TransactionFrameTracerTest; + friend class TransactionSurfaceFrameTest; + + bool getAutoRefresh() const { return mDrawingState.autoRefresh; } + bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } + + std::atomic<bool> mSidebandStreamChanged{false}; // Returns true if the layer can draw shadows on its border. virtual bool canDrawShadows() const { return true; } @@ -1088,6 +994,46 @@ private: // Fills in the frame and transform info for the gui::WindowInfo. void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay); + inline void tracePendingBufferCount(int32_t pendingBuffers); + + // Latch sideband stream and returns true if the dirty region should be updated. + bool latchSidebandStream(bool& recomputeVisibleRegions); + + bool hasFrameUpdate() const; + + void updateTexImage(nsecs_t latchTime); + + // Crop that applies to the buffer + Rect computeBufferCrop(const State& s); + + bool willPresentCurrentTransaction() const; + + // Returns true if the transformed buffer size does not match the layer size and we need + // to apply filtering. + bool bufferNeedsFiltering() const; + + void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, + const sp<GraphicBuffer>& buffer, uint64_t framenumber, + const sp<Fence>& releaseFence, + uint32_t currentMaxAcquiredBufferCount); + + // Returns true if there is a valid color to fill. + bool fillsColor() const; + // Returns true if this layer has a blur value. + bool hasBlur() const; + bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); } + bool hasBufferOrSidebandStream() const { + return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr)); + } + + bool hasBufferOrSidebandStreamInDrawing() const { + return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr)); + } + + bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); } + + void updateChildrenSnapshots(bool updateGeometry); + // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is // a transform from the current layer coordinate space to display(screen) coordinate space. @@ -1106,11 +1052,6 @@ private: bool mGetHandleCalled = false; - // Tracks the process and user id of the caller when creating this layer - // to help debugging. - pid_t mCallingPid; - uid_t mCallingUid; - // The current layer is a clone of mClonedFrom. This means that this layer will update it's // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers, // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, @@ -1131,7 +1072,80 @@ private: bool mIsAtRoot = false; uint32_t mLayerCreationFlags; + bool findInHierarchy(const sp<Layer>&); + + bool mBorderEnabled = false; + float mBorderWidth; + half4 mBorderColor; + + void setTransformHint(ui::Transform::RotationFlags); + + const uint32_t mTextureName; + + // Transform hint provided to the producer. This must be accessed holding + // the mStateLock. + ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0; + bool mSkipReportingTransformHint = true; + + ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; + uint64_t mPreviousReleasedFrameNumber = 0; + + uint64_t mPreviousBarrierFrameNumber = 0; + + bool mReleasePreviousBuffer = false; + + // Stores the last set acquire fence signal time used to populate the callback handle's acquire + // time. + std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1; + + std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications; + // An upper bound on the number of SurfaceFrames in the pending classifications deque. + static constexpr int kPendingClassificationMaxSurfaceFrames = 50; + + const std::string mBlastTransactionName{"BufferTX - " + mName}; + // This integer is incremented everytime a buffer arrives at the server for this layer, + // and decremented when a buffer is dropped or latched. When changed the integer is exported + // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is + // possible to see when a buffer arrived at the server, and in which frame it latched. + // + // You can understand the trace this way: + // - If the integer increases, a buffer arrived at the server. + // - If the integer decreases in latchBuffer, that buffer was latched + // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped + std::atomic<int32_t> mPendingBufferTransactions{0}; + + // Contains requested position and matrix updates. This will be applied if the client does + // not specify a destination frame. + ui::Transform mRequestedTransform; + + sp<LayerFE> mLayerFE; + std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>(); + + friend class LayerSnapshotGuard; +}; + +// LayerSnapshotGuard manages the movement of LayerSnapshot between a Layer and its corresponding +// LayerFE. This class must be used whenever LayerFEs are passed to CompositionEngine. Instances of +// LayerSnapshotGuard should only be constructed on the main thread and should not be moved outside +// the main thread. +// +// Moving the snapshot instead of sharing common state prevents use of LayerFE outside the main +// thread by making errors obvious (i.e. use outside the main thread results in SEGFAULTs due to +// nullptr dereference). +class LayerSnapshotGuard { +public: + LayerSnapshotGuard(Layer* layer) REQUIRES(kMainThreadContext); + ~LayerSnapshotGuard() REQUIRES(kMainThreadContext); + + LayerSnapshotGuard(const LayerSnapshotGuard&) = delete; + LayerSnapshotGuard& operator=(const LayerSnapshotGuard&) = delete; + + LayerSnapshotGuard(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext); + LayerSnapshotGuard& operator=(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext); + +private: + Layer* mLayer; }; std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate); diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp new file mode 100644 index 0000000000..363adc641e --- /dev/null +++ b/services/surfaceflinger/LayerFE.cpp @@ -0,0 +1,386 @@ +/* + * Copyright 2022 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 "LayerFE" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <gui/GLConsumer.h> +#include <gui/TraceUtils.h> +#include <math/vec3.h> +#include <system/window.h> +#include <utils/Log.h> + +#include "DisplayDevice.h" +#include "LayerFE.h" + +namespace android { + +namespace { +constexpr float defaultMaxLuminance = 1000.0; + +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); + const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + mat4 tr; + + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + tr = tr * rot90; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + tr = tr * flipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + tr = tr * flipV; + } + return inverse(tr); +} + +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(); +} + +// Computes the transform matrix using the setFilteringEnabled to determine whether the +// transform matrix should be computed for use with bilinear filtering. +void getDrawingTransformMatrix(const std::shared_ptr<renderengine::ExternalTexture>& buffer, + Rect bufferCrop, uint32_t bufferTransform, bool filteringEnabled, + float outMatrix[16]) { + if (!buffer) { + ALOGE("Buffer should not be null!"); + return; + } + GLConsumer::computeTransformMatrix(outMatrix, static_cast<float>(buffer->getWidth()), + static_cast<float>(buffer->getHeight()), + buffer->getPixelFormat(), bufferCrop, bufferTransform, + filteringEnabled); +} + +} // namespace + +LayerFE::LayerFE(const std::string& name) : mName(name) {} + +const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const { + return mSnapshot.get(); +} + +bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) { + mCompositionResult.refreshStartTime = refreshStartTime; + return mSnapshot->hasReadyFrame; +} + +std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { + std::optional<compositionengine::LayerFE::LayerSettings> layerSettings = + prepareClientCompositionInternal(targetSettings); + // Nothing to render. + if (!layerSettings) { + return {}; + } + + // HWC requests to clear this layer. + if (targetSettings.clearContent) { + prepareClearClientComposition(*layerSettings, false /* blackout */); + return layerSettings; + } + + // set the shadow for the layer if needed + prepareShadowClientComposition(*layerSettings, targetSettings.viewport); + + return layerSettings; +} + +std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal( + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { + ATRACE_CALL(); + compositionengine::LayerFE::LayerSettings layerSettings; + layerSettings.geometry.boundaries = + reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint); + layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4(); + + // skip drawing content if the targetSettings indicate the content will be occluded + const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent; + layerSettings.skipContentDraw = !drawContent; + + if (!mSnapshot->colorTransformIsIdentity) { + layerSettings.colorTransform = mSnapshot->colorTransform; + } + + const auto& roundedCornerState = mSnapshot->roundedCorner; + layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius; + layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect; + + layerSettings.alpha = mSnapshot->alpha; + layerSettings.sourceDataspace = mSnapshot->dataspace; + + // Override the dataspace transfer from 170M to sRGB if the device configuration requests this. + // We do this here instead of in buffer info so that dumpsys can still report layers that are + // using the 170M transfer. + if (targetSettings.treat170mAsSrgb && + (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) == + HAL_DATASPACE_TRANSFER_SMPTE_170M) { + layerSettings.sourceDataspace = static_cast<ui::Dataspace>( + (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) | + (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) | + HAL_DATASPACE_TRANSFER_SRGB); + } + + layerSettings.whitePointNits = targetSettings.whitePointNits; + switch (targetSettings.blurSetting) { + case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled: + layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; + layerSettings.blurRegions = mSnapshot->blurRegions; + layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); + break; + case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: + layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; + break; + case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly: + layerSettings.blurRegions = mSnapshot->blurRegions; + layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); + break; + case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled: + default: + break; + } + layerSettings.stretchEffect = mSnapshot->stretchEffect; + // Record the name of the layer for debugging further down the stack. + layerSettings.name = mSnapshot->name; + + if (hasEffect() && !hasBufferOrSidebandStream()) { + prepareEffectsClientComposition(layerSettings, targetSettings); + return layerSettings; + } + + prepareBufferStateClientComposition(layerSettings, targetSettings); + return layerSettings; +} + +void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, + bool blackout) const { + layerSettings.source.buffer.buffer = nullptr; + layerSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f); + layerSettings.disableBlending = true; + layerSettings.bufferId = 0; + layerSettings.frameNumber = 0; + + // If layer is blacked out, force alpha to 1 so that we draw a black color layer. + layerSettings.alpha = blackout ? 1.0f : 0.0f; + layerSettings.name = mSnapshot->name; +} + +void LayerFE::prepareEffectsClientComposition( + compositionengine::LayerFE::LayerSettings& layerSettings, + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { + // If fill bounds are occluded or the fill color is invalid skip the fill settings. + if (targetSettings.realContentIsVisible && fillsColor()) { + // Set color for color fill settings. + layerSettings.source.solidColor = mSnapshot->color.rgb; + } else if (hasBlur() || drawShadows()) { + layerSettings.skipContentDraw = true; + } +} + +void LayerFE::prepareBufferStateClientComposition( + compositionengine::LayerFE::LayerSettings& layerSettings, + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { + ATRACE_CALL(); + if (CC_UNLIKELY(!mSnapshot->externalTexture)) { + // If there is no buffer for the layer or we have sidebandstream where there is no + // activeBuffer, then we need to return LayerSettings. + return; + } + const bool blackOutLayer = + (mSnapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) || + ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure); + const bool bufferCanBeUsedAsHwTexture = + mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE; + if (blackOutLayer || !bufferCanBeUsedAsHwTexture) { + ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable", + mSnapshot->name.c_str()); + prepareClearClientComposition(layerSettings, true /* blackout */); + return; + } + + layerSettings.source.buffer.buffer = mSnapshot->externalTexture; + layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque; + layerSettings.source.buffer.fence = mSnapshot->acquireFence; + layerSettings.source.buffer.textureName = mSnapshot->textureName; + layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha; + layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410; + bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086; + bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3; + float maxLuminance = 0.f; + if (hasSmpte2086 && hasCta861_3) { + maxLuminance = std::min(mSnapshot->hdrMetadata.smpte2086.maxLuminance, + mSnapshot->hdrMetadata.cta8613.maxContentLightLevel); + } else if (hasSmpte2086) { + maxLuminance = mSnapshot->hdrMetadata.smpte2086.maxLuminance; + } else if (hasCta861_3) { + maxLuminance = mSnapshot->hdrMetadata.cta8613.maxContentLightLevel; + } else { + switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + case HAL_DATASPACE_TRANSFER_HLG: + // Behavior-match previous releases for HDR content + maxLuminance = defaultMaxLuminance; + break; + } + } + layerSettings.source.buffer.maxLuminanceNits = maxLuminance; + layerSettings.frameNumber = mSnapshot->frameNumber; + layerSettings.bufferId = mSnapshot->externalTexture->getId(); + + const bool useFiltering = targetSettings.needsFiltering || + mSnapshot->geomLayerTransform.needsBilinearFiltering() || + mSnapshot->bufferNeedsFiltering; + + // Query the texture matrix given our current filtering mode. + float textureMatrix[16]; + getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop, + mSnapshot->geomBufferTransform, useFiltering, textureMatrix); + + if (mSnapshot->geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to + * the texture transform + */ + uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags(); + mat4 tr = inverseOrientation(transform); + + /** + * TODO(b/36727915): This is basically a hack. + * + * Ensure that regardless of the parent transformation, + * this buffer is always transformed from native display + * orientation to display orientation. For example, in the case + * of a camera where the buffer remains in native orientation, + * we want the pixels to always be upright. + */ + const auto parentTransform = mSnapshot->transform; + tr = tr * inverseOrientation(parentTransform.getOrientation()); + + // and finally apply it to the original texture matrix + const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr); + memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); + } + + const Rect win{layerSettings.geometry.boundaries}; + float bufferWidth = static_cast<float>(mSnapshot->bufferSize.getWidth()); + float bufferHeight = static_cast<float>(mSnapshot->bufferSize.getHeight()); + + // Layers 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 (!mSnapshot->bufferSize.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; + const float translateY = float(win.top) / bufferHeight; + const float translateX = float(win.left) / bufferWidth; + + // Flip y-coordinates because GLConsumer expects OpenGL convention. + mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) * + mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) * + mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) * + mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f)); + + layerSettings.source.buffer.useTextureFiltering = useFiltering; + layerSettings.source.buffer.textureTransform = + mat4(static_cast<const float*>(textureMatrix)) * tr; + + return; +} + +void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster, + const Rect& layerStackRect) const { + renderengine::ShadowSettings state = mSnapshot->shadowSettings; + if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) { + return; + } + + // Shift the spot light x-position to the middle of the display and then + // offset it by casting layer's screen pos. + state.lightPos.x = + (static_cast<float>(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left; + state.lightPos.y -= mSnapshot->transformedBounds.top; + caster.shadow = state; +} + +void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { + mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult)); +} + +CompositionResult&& LayerFE::stealCompositionResult() { + return std::move(mCompositionResult); +} + +const char* LayerFE::getDebugName() const { + return mName.c_str(); +} + +const LayerMetadata* LayerFE::getMetadata() const { + return &mSnapshot->layerMetadata; +} + +const LayerMetadata* LayerFE::getRelativeMetadata() const { + return &mSnapshot->relativeLayerMetadata; +} + +int32_t LayerFE::getSequence() const { + return mSnapshot->sequence; +} + +bool LayerFE::hasRoundedCorners() const { + return mSnapshot->roundedCorner.hasRoundedCorners(); +} + +void LayerFE::setWasClientComposed(const sp<Fence>& fence) { + mCompositionResult.lastClientCompositionFence = fence; +} + +bool LayerFE::hasBufferOrSidebandStream() const { + return mSnapshot->externalTexture || mSnapshot->sidebandStream; +} + +bool LayerFE::fillsColor() const { + return mSnapshot->color.r >= 0.0_hf && mSnapshot->color.g >= 0.0_hf && + mSnapshot->color.b >= 0.0_hf; +} + +bool LayerFE::hasBlur() const { + return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0; +} + +bool LayerFE::drawShadows() const { + return mSnapshot->shadowSettings.length > 0.f && + (mSnapshot->shadowSettings.ambientColor.a > 0 || + mSnapshot->shadowSettings.spotColor.a > 0); +}; + +const sp<GraphicBuffer> LayerFE::getBuffer() const { + return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr; +} + +} // namespace android diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h new file mode 100644 index 0000000000..822bcb7a94 --- /dev/null +++ b/services/surfaceflinger/LayerFE.h @@ -0,0 +1,119 @@ +/* + * Copyright 2022 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 <gui/LayerMetadata.h> + +#include "compositionengine/LayerFE.h" +#include "compositionengine/LayerFECompositionState.h" +#include "renderengine/LayerSettings.h" + +namespace android { +struct RoundedCornerState { + RoundedCornerState() = default; + RoundedCornerState(const FloatRect& cropRect, const vec2& radius) + : cropRect(cropRect), radius(radius) {} + + // Rounded rectangle in local layer coordinate space. + FloatRect cropRect = FloatRect(); + // Radius of the rounded rectangle. + vec2 radius; + bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; } +}; + +// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition +// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings +// passed to Render Engine are created using properties stored on this struct. +struct LayerSnapshot : public compositionengine::LayerFECompositionState { + int32_t sequence; + std::string name; + uint32_t textureName; + bool contentOpaque; + RoundedCornerState roundedCorner; + StretchEffect stretchEffect; + FloatRect transformedBounds; + renderengine::ShadowSettings shadowSettings; + bool premultipliedAlpha; + bool isHdrY410; + bool bufferNeedsFiltering; + ui::Transform transform; + Rect bufferSize; + std::shared_ptr<renderengine::ExternalTexture> externalTexture; + gui::LayerMetadata layerMetadata; + gui::LayerMetadata relativeLayerMetadata; + bool contentDirty; + bool hasReadyFrame; + ui::Transform blurRegionTransform; +}; + +struct CompositionResult { + // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition + // and remove this field. + nsecs_t refreshStartTime = 0; + std::vector<ftl::SharedFuture<FenceResult>> releaseFences; + sp<Fence> lastClientCompositionFence = nullptr; +}; + +class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE { +public: + LayerFE(const std::string& name); + + // compositionengine::LayerFE overrides + const compositionengine::LayerFECompositionState* getCompositionState() const override; + bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override; + void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; + const char* getDebugName() const override; + int32_t getSequence() const override; + bool hasRoundedCorners() const override; + void setWasClientComposed(const sp<Fence>&) override; + const gui::LayerMetadata* getMetadata() const override; + const gui::LayerMetadata* getRelativeMetadata() const override; + std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings&) const; + CompositionResult&& stealCompositionResult(); + + std::unique_ptr<LayerSnapshot> mSnapshot; + +private: + std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal( + compositionengine::LayerFE::ClientCompositionTargetSettings&) const; + // Modifies the passed in layer settings to clear the contents. If the blackout flag is set, + // the settings clears the content with a solid black fill. + void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const; + void prepareShadowClientComposition(LayerFE::LayerSettings& caster, + const Rect& layerStackRect) const; + void prepareBufferStateClientComposition( + compositionengine::LayerFE::LayerSettings&, + compositionengine::LayerFE::ClientCompositionTargetSettings&) const; + void prepareEffectsClientComposition( + compositionengine::LayerFE::LayerSettings&, + compositionengine::LayerFE::ClientCompositionTargetSettings&) const; + + bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); } + bool hasBufferOrSidebandStream() const; + + bool fillsColor() const; + bool hasBlur() const; + bool drawShadows() const; + + const sp<GraphicBuffer> getBuffer() const; + + CompositionResult mCompositionResult; + std::string mName; +}; + +} // namespace android diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp deleted file mode 100644 index 1c0263ba0b..0000000000 --- a/services/surfaceflinger/LayerRejecter.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include "LayerRejecter.h" - -#include <gui/BufferItem.h> -#include <system/window.h> - -#define DEBUG_RESIZE 0 - -namespace android { - -LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current, - bool& recomputeVisibleRegions, bool stickySet, const std::string& name, - bool transformToDisplayInverse) - : mFront(front), - mCurrent(current), - mRecomputeVisibleRegions(recomputeVisibleRegions), - mStickyTransformSet(stickySet), - mName(name), - mTransformToDisplayInverse(transformToDisplayInverse) {} - -bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) { - if (buf == nullptr) { - return false; - } - - uint32_t bufWidth = buf->getWidth(); - uint32_t bufHeight = buf->getHeight(); - - // check that we received a buffer of the right size - // (Take the buffer's orientation into account) - if (item.mTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - - if (mTransformToDisplayInverse) { - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - } - - int actualScalingMode = item.mScalingMode; - bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE; - if (mFront.active_legacy != mFront.requested_legacy) { - if (isFixedSize || - (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) { - // Here we pretend the transaction happened by updating the - // current and drawing states. Drawing state is only accessed - // in this thread, no need to have it locked - mFront.active_legacy = mFront.requested_legacy; - - // We also need to update the current state so that - // we don't end-up overwriting the drawing state with - // this stale current state during the next transaction - // - // NOTE: We don't need to hold the transaction lock here - // because State::active_legacy is only accessed from this thread. - mCurrent.active_legacy = mFront.active_legacy; - mCurrent.modified = true; - - // recompute visible region - mRecomputeVisibleRegions = true; - - if (mFront.crop != mFront.requestedCrop) { - mFront.crop = mFront.requestedCrop; - mCurrent.crop = mFront.requestedCrop; - mRecomputeVisibleRegions = true; - } - } - - ALOGD_IF(DEBUG_RESIZE, - "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n" - " drawing={ active_legacy ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} " - "(%4d,%4d) " - "}\n" - " requested_legacy={ wh={%4u,%4u} }}\n", - mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode, - mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top, - mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(), - mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h); - } - - if (!isFixedSize && !mStickyTransformSet) { - if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) { - // reject this buffer - ALOGE("[%s] rejecting buffer: " - "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}", - mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w, - mFront.active_legacy.h); - return true; - } - } - - // if the transparent region has changed (this test is - // conservative, but that's fine, worst case we're doing - // a bit of extra work), we latch the new one and we - // trigger a visible-region recompute. - // - // We latch the transparent region here, instead of above where we latch - // the rest of the geometry because it is only content but not necessarily - // resize dependent. - if (!mFront.activeTransparentRegion_legacy.hasSameRects( - mFront.requestedTransparentRegion_legacy)) { - mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy; - - // We also need to update the current state so that - // we don't end-up overwriting the drawing state with - // this stale current state during the next transaction - // - // NOTE: We don't need to hold the transaction lock here - // because State::active_legacy is only accessed from this thread. - mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy; - - // recompute visible region - mRecomputeVisibleRegions = true; - } - - return false; -} - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h deleted file mode 100644 index 4981f451d9..0000000000 --- a/services/surfaceflinger/LayerRejecter.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007 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 "Layer.h" -#include "BufferLayerConsumer.h" - -namespace android { - -class LayerRejecter : public BufferLayerConsumer::BufferRejecter { -public: - LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions, - bool stickySet, const std::string& name, - bool transformToDisplayInverse); - - virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&); - -private: - Layer::State& mFront; - Layer::State& mCurrent; - bool& mRecomputeVisibleRegions; - const bool mStickyTransformSet; - const std::string& mName; - const bool mTransformToDisplayInverse; -}; - -} // namespace android diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp index 896f25404d..554fae401e 100644 --- a/services/surfaceflinger/LayerRenderArea.cpp +++ b/services/surfaceflinger/LayerRenderArea.cpp @@ -17,8 +17,8 @@ #include <ui/GraphicTypes.h> #include <ui/Transform.h> -#include "ContainerLayer.h" #include "DisplayDevice.h" +#include "FrontEnd/LayerCreationArgs.h" #include "Layer.h" #include "LayerRenderArea.h" #include "SurfaceFlinger.h" @@ -31,6 +31,7 @@ void reparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent, // Compute and cache the bounds for the new parent layer. newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(), 0.f /* shadowRadius */); + newParent->updateSnapshot(true /* updateGeometry */); oldParent->setChildrenDrawingParent(newParent); }; @@ -110,8 +111,9 @@ void LayerRenderArea::render(std::function<void()> drawLayers) { // layer which has no properties set and which does not draw. // We hold the statelock as the reparent-for-drawing operation modifies the // hierarchy and there could be readers on Binder threads, like dump. - sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer( - {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()}); + auto screenshotParentLayer = mFlinger.getFactory().createEffectLayer( + {&mFlinger, nullptr, "Screenshot Parent"s, ISurfaceComposerClient::eNoColorFill, + LayerMetadata()}); { Mutex::Autolock _l(mFlinger.mStateLock); reparentForDrawing(mLayer, screenshotParentLayer, sourceCrop); diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h new file mode 100644 index 0000000000..6b633607ae --- /dev/null +++ b/services/surfaceflinger/LocklessQueue.h @@ -0,0 +1,82 @@ +/* + * Copyright 2022 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 <atomic> +#include <optional> + +template <typename T> +// Single consumer multi producer stack. We can understand the two operations independently to see +// why they are without race condition. +// +// push is responsible for maintaining a linked list stored in mPush, and called from multiple +// threads without lock. We can see that if two threads never observe the same value from +// mPush.load, it just functions as a normal linked list. In the case where two threads observe the +// same value, one of them has to execute the compare_exchange first. The one that doesn't execute +// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by +// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to +// see that the process can repeat with an arbitrary number of threads. +// +// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges +// the entire push list with null. This is safe, since the only other reader (push) +// of mPush will retry if it changes in between it's read and atomic compare. We +// then store the list and pop one element. +// +// If we already had something in the pop list we just pop directly. +class LocklessQueue { +public: + class Entry { + public: + T mValue; + std::atomic<Entry*> mNext; + Entry(T value) : mValue(value) {} + }; + std::atomic<Entry*> mPush = nullptr; + std::atomic<Entry*> mPop = nullptr; + bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); } + + void push(T value) { + Entry* entry = new Entry(value); + Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/); + do { + entry->mNext = previousHead; + } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/ + } + std::optional<T> pop() { + Entry* popped = mPop.load(/*std::memory_order_acquire*/); + if (popped) { + // Single consumer so this is fine + mPop.store(popped->mNext /* , std::memory_order_release */); + auto value = popped->mValue; + delete popped; + return std::move(value); + } else { + Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */); + if (!grabbedList) return std::nullopt; + // Reverse the list + while (grabbedList->mNext) { + Entry* next = grabbedList->mNext; + grabbedList->mNext = popped; + popped = grabbedList; + grabbedList = next; + } + mPop.store(popped /* , std::memory_order_release */); + auto value = grabbedList->mValue; + delete grabbedList; + return std::move(value); + } + } +}; diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp deleted file mode 100644 index df76f50112..0000000000 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2014 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include "MonitoredProducer.h" -#include "Layer.h" -#include "SurfaceFlinger.h" - -#include "Scheduler/MessageQueue.h" - -namespace android { - -MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer, - const sp<SurfaceFlinger>& flinger, - const wp<Layer>& layer) : - mProducer(producer), - mFlinger(flinger), - mLayer(layer) {} - -MonitoredProducer::~MonitoredProducer() {} - -status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - return mProducer->requestBuffer(slot, buf); -} - -status_t MonitoredProducer::setMaxDequeuedBufferCount( - int maxDequeuedBuffers) { - return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); -} - -status_t MonitoredProducer::setAsyncMode(bool async) { - return mProducer->setAsyncMode(async); -} - -status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, - PixelFormat format, uint64_t usage, - uint64_t* outBufferAge, - FrameEventHistoryDelta* outTimestamps) { - return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); -} - -status_t MonitoredProducer::detachBuffer(int slot) { - return mProducer->detachBuffer(slot); -} - -status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence) { - return mProducer->detachNextBuffer(outBuffer, outFence); -} - -status_t MonitoredProducer::attachBuffer(int* outSlot, - const sp<GraphicBuffer>& buffer) { - return mProducer->attachBuffer(outSlot, buffer); -} - -status_t MonitoredProducer::queueBuffer(int slot, const QueueBufferInput& input, - QueueBufferOutput* output) { - return mProducer->queueBuffer(slot, input, output); -} - -status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) { - return mProducer->cancelBuffer(slot, fence); -} - -int MonitoredProducer::query(int what, int* value) { - return mProducer->query(what, value); -} - -status_t MonitoredProducer::connect(const sp<IProducerListener>& listener, - int api, bool producerControlledByApp, QueueBufferOutput* output) { - return mProducer->connect(listener, api, producerControlledByApp, output); -} - -status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) { - return mProducer->disconnect(api, mode); -} - -status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) { - return mProducer->setSidebandStream(stream); -} - -void MonitoredProducer::allocateBuffers(uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage) { - mProducer->allocateBuffers(width, height, format, usage); -} - -status_t MonitoredProducer::allowAllocation(bool allow) { - return mProducer->allowAllocation(allow); -} - -status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) { - return mProducer->setGenerationNumber(generationNumber); -} - -String8 MonitoredProducer::getConsumerName() const { - return mProducer->getConsumerName(); -} - -status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) { - return mProducer->setSharedBufferMode(sharedBufferMode); -} - -status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) { - return mProducer->setAutoRefresh(autoRefresh); -} - -status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) { - return mProducer->setDequeueTimeout(timeout); -} - -status_t MonitoredProducer::setLegacyBufferDrop(bool drop) { - return mProducer->setLegacyBufferDrop(drop); -} - -status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence, float outTransformMatrix[16]) { - return mProducer->getLastQueuedBuffer(outBuffer, outFence, - outTransformMatrix); -} - -status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, - Rect* outRect, uint32_t* outTransform) { - return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform); -} - -void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { - mProducer->getFrameTimestamps(outDelta); -} - -status_t MonitoredProducer::getUniqueId(uint64_t* outId) const { - return mProducer->getUniqueId(outId); -} - -status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const { - return mProducer->getConsumerUsage(outUsage); -} - -status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) { - return mProducer->setAutoPrerotation(autoPrerotation); -} - -IBinder* MonitoredProducer::onAsBinder() { - return this; -} - -sp<Layer> MonitoredProducer::getLayer() const { - return mLayer.promote(); -} - -// --------------------------------------------------------------------------- -}; // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h deleted file mode 100644 index 3778277fd3..0000000000 --- a/services/surfaceflinger/MonitoredProducer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2014 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_MONITORED_PRODUCER_H -#define ANDROID_MONITORED_PRODUCER_H - -#include <gui/IGraphicBufferProducer.h> - -namespace android { - -class IProducerListener; -class NativeHandle; -class SurfaceFlinger; -class Layer; - -// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will -// be notified upon its destruction -class MonitoredProducer : public BnGraphicBufferProducer { -public: - MonitoredProducer(const sp<IGraphicBufferProducer>& producer, - const sp<SurfaceFlinger>& flinger, - const wp<Layer>& layer); - virtual ~MonitoredProducer(); - - // From IGraphicBufferProducer - virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); - virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); - virtual status_t setAsyncMode(bool async); - virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, - PixelFormat format, uint64_t usage, uint64_t* outBufferAge, - FrameEventHistoryDelta* outTimestamps); - virtual status_t detachBuffer(int slot); - virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence); - virtual status_t attachBuffer(int* outSlot, - const sp<GraphicBuffer>& buffer); - virtual status_t queueBuffer(int slot, const QueueBufferInput& input, - QueueBufferOutput* output); - virtual status_t cancelBuffer(int slot, const sp<Fence>& fence); - virtual int query(int what, int* value); - virtual status_t connect(const sp<IProducerListener>& token, int api, - bool producerControlledByApp, QueueBufferOutput* output); - virtual status_t disconnect(int api, DisconnectMode mode); - virtual status_t setSidebandStream(const sp<NativeHandle>& stream); - virtual void allocateBuffers(uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage); - virtual status_t allowAllocation(bool allow); - virtual status_t setGenerationNumber(uint32_t generationNumber); - virtual String8 getConsumerName() const override; - virtual status_t setDequeueTimeout(nsecs_t timeout) override; - virtual status_t setLegacyBufferDrop(bool drop) override; - virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence, float outTransformMatrix[16]) override; - virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, - Rect* outRect, uint32_t* outTransform) override; - virtual IBinder* onAsBinder(); - virtual status_t setSharedBufferMode(bool sharedBufferMode) override; - virtual status_t setAutoRefresh(bool autoRefresh) 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; - virtual status_t setAutoPrerotation(bool autoPrerotation) override; - - // The Layer which created this producer, and on which queued Buffer's will be displayed. - sp<Layer> getLayer() const; - -private: - sp<IGraphicBufferProducer> mProducer; - sp<SurfaceFlinger> mFlinger; - // The Layer which created this producer, and on which queued Buffer's will be displayed. - wp<Layer> mLayer; -}; - -}; // namespace android - -#endif // ANDROID_MONITORED_PRODUCER_H diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp index 3fff9283ee..a6a3eec8ff 100644 --- a/services/surfaceflinger/NativeWindowSurface.cpp +++ b/services/surfaceflinger/NativeWindowSurface.cpp @@ -30,7 +30,7 @@ std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( class NativeWindowSurface final : public surfaceflinger::NativeWindowSurface { public: explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer) - : mSurface(new Surface(producer, /* controlledByApp */ false)) {} + : mSurface(sp<Surface>::make(producer, /* controlledByApp */ false)) {} ~NativeWindowSurface() override = default; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index a9180d4483..7aa7e17b3b 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -42,8 +42,8 @@ constexpr int kDigitWidth = 64; constexpr int kDigitHeight = 100; constexpr int kDigitSpace = 16; -// Layout is digit, space, digit, space, digit, space, spinner. -constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace; +constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1; +constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace; constexpr int kBufferHeight = kDigitHeight; SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) { @@ -121,16 +121,10 @@ void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkCo drawSegment(Segment::Bottom, left, color, canvas); } -auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, +auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color, ui::Transform::RotationFlags rotation, - bool showSpinner) -> Buffers { - if (number < 0 || number > 1000) return {}; - - const auto hundreds = number / 100; - const auto tens = (number / 10) % 10; - const auto ones = number % 10; - - const size_t loopCount = showSpinner ? 6 : 1; + ftl::Flags<Features> features) -> Buffers { + const size_t loopCount = features.test(Features::Spinner) ? 6 : 1; Buffers buffers; buffers.reserve(loopCount); @@ -152,13 +146,13 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, } }(); - sp<GraphicBuffer> buffer = - new GraphicBuffer(static_cast<uint32_t>(bufferWidth), - static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | - GRALLOC_USAGE_HW_TEXTURE, - "RefreshRateOverlayBuffer"); + const auto kUsageFlags = + static_cast<uint64_t>(GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | + GRALLOC_USAGE_HW_TEXTURE); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), + static_cast<uint32_t>(bufferHeight), + HAL_PIXEL_FORMAT_RGBA_8888, 1u, + kUsageFlags, "RefreshRateOverlayBuffer"); const status_t bufferStatus = buffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d", @@ -169,20 +163,9 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, canvas->setMatrix(canvasTransform); int left = 0; - if (hundreds != 0) { - drawDigit(hundreds, left, color, *canvas); - } - left += kDigitWidth + kDigitSpace; - - if (tens != 0) { - drawDigit(tens, left, color, *canvas); - } - left += kDigitWidth + kDigitSpace; - - drawDigit(ones, left, color, *canvas); - left += kDigitWidth + kDigitSpace; - - if (showSpinner) { + drawNumber(displayFps, left, color, *canvas); + left += 3 * (kDigitWidth + kDigitSpace); + if (features.test(Features::Spinner)) { switch (i) { case 0: drawSegment(Segment::Upper, left, color, *canvas); @@ -205,6 +188,13 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, } } + left += kDigitWidth + kDigitSpace; + + if (features.test(Features::RenderRate)) { + drawNumber(renderFps, left, color, *canvas); + } + left += 3 * (kDigitWidth + kDigitSpace); + void* pixels = nullptr; buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); @@ -219,6 +209,23 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, return buffers; } +void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color, + SkCanvas& canvas) { + if (number < 0 || number >= 1000) return; + + if (number >= 100) { + drawDigit(number / 100, left, color, canvas); + } + left += kDigitWidth + kDigitSpace; + + if (number >= 10) { + drawDigit((number / 10) % 10, left, color, canvas); + } + left += kDigitWidth + kDigitSpace; + + drawDigit(number % 10, left, color, canvas); +} + std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() { sp<SurfaceControl> surfaceControl = SurfaceComposerClient::getDefault() @@ -228,10 +235,8 @@ std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() { return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl)); } -RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner) - : mFpsRange(fpsRange), - mShowSpinner(showSpinner), - mSurfaceControl(createSurfaceControlHolder()) { +RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features) + : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) { if (!mSurfaceControl) { ALOGE("%s: Failed to create buffer state layer", __func__); return; @@ -243,10 +248,15 @@ RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner) .apply(); } -auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& { +auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& { static const Buffers kNoBuffers; if (!mSurfaceControl) return kNoBuffers; + // avoid caching different render rates if RenderRate is anyway not visible + if (!mFeatures.test(Features::RenderRate)) { + renderFps = 0_Hz; + } + const auto transformHint = static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint()); @@ -266,17 +276,20 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& { .setTransform(mSurfaceControl->get(), transform) .apply(); - BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint}); + BufferCache::const_iterator it = + mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint}); if (it == mBufferCache.end()) { const int minFps = mFpsRange.min.getIntValue(); const int maxFps = mFpsRange.max.getIntValue(); - // Clamp to the range. The current fps may be outside of this range if the display has - // changed its set of supported refresh rates. - const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps); + // Clamp to the range. The current displayFps may be outside of this range if the display + // has changed its set of supported refresh rates. + const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps); + const int renderIntFps = renderFps.getIntValue(); // Ensure non-zero range to avoid division by zero. - const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps); + const float fpsScale = + static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps); constexpr SkColor kMinFpsColor = SK_ColorRED; constexpr SkColor kMaxFpsColor = SK_ColorGREEN; @@ -292,8 +305,11 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& { const SkColor color = colorBase.toSkColor(); - auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner); - it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first; + auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint, + mFeatures); + it = mBufferCache + .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers)) + .first; } return it->second; @@ -303,7 +319,7 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { constexpr int32_t kMaxWidth = 1000; const auto width = std::min({kMaxWidth, viewport.width, viewport.height}); const auto height = 2 * width; - Rect frame((3 * width) >> 4, height >> 5); + Rect frame((5 * width) >> 4, height >> 5); frame.offsetBy(width >> 5, height >> 4); createTransaction(mSurfaceControl->get()) @@ -317,16 +333,17 @@ void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply(); } -void RefreshRateOverlay::changeRefreshRate(Fps fps) { - mCurrentFps = fps; - const auto buffer = getOrCreateBuffers(fps)[mFrame]; +void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) { + mDisplayFps = displayFps; + mRenderFps = renderFps; + const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame]; createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply(); } void RefreshRateOverlay::animate() { - if (!mShowSpinner || !mCurrentFps) return; + if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return; - const auto& buffers = getOrCreateBuffers(*mCurrentFps); + const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps); mFrame = (mFrame + 1) % buffers.size(); const auto buffer = buffers[mFrame]; createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply(); diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index a2966e654e..d6f828f7f1 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -19,6 +19,7 @@ #include <SkColor.h> #include <vector> +#include <ftl/flags.h> #include <ftl/small_map.h> #include <ui/LayerStack.h> #include <ui/Size.h> @@ -50,11 +51,16 @@ private: class RefreshRateOverlay { public: - RefreshRateOverlay(FpsRange, bool showSpinner); + enum class Features { + Spinner = 1 << 0, + RenderRate = 1 << 1, + }; + + RefreshRateOverlay(FpsRange, ftl::Flags<Features>); void setLayerStack(ui::LayerStack); void setViewport(ui::Size); - void changeRefreshRate(Fps); + void changeRefreshRate(Fps, Fps); void animate(); private: @@ -62,32 +68,39 @@ private: class SevenSegmentDrawer { public: - static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner); + static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags, + ftl::Flags<Features>); private: enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom }; static void drawSegment(Segment, int left, SkColor, SkCanvas&); static void drawDigit(int digit, int left, SkColor, SkCanvas&); + static void drawNumber(int number, int left, SkColor, SkCanvas&); }; - const Buffers& getOrCreateBuffers(Fps); + const Buffers& getOrCreateBuffers(Fps, Fps); struct Key { - int fps; + int displayFps; + int renderFps; ui::Transform::RotationFlags flags; - bool operator==(Key other) const { return fps == other.fps && flags == other.flags; } + bool operator==(Key other) const { + return displayFps == other.displayFps && renderFps == other.renderFps && + flags == other.flags; + } }; using BufferCache = ftl::SmallMap<Key, Buffers, 9>; BufferCache mBufferCache; - std::optional<Fps> mCurrentFps; + std::optional<Fps> mDisplayFps; + std::optional<Fps> mRenderFps; size_t mFrame = 0; const FpsRange mFpsRange; // For color interpolation. - const bool mShowSpinner; + const ftl::Flags<Features> mFeatures; const std::unique_ptr<SurfaceControlHolder> mSurfaceControl; }; diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 2487dbd793..6e64e0a13b 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -40,6 +40,7 @@ #include "DisplayDevice.h" #include "DisplayRenderArea.h" +#include "FrontEnd/LayerCreationArgs.h" #include "Layer.h" #include "Scheduler/VsyncController.h" #include "SurfaceFlinger.h" @@ -129,12 +130,12 @@ RegionSamplingThread::~RegionSamplingThread() { } } -void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer, +void RegionSamplingThread::addListener(const Rect& samplingArea, uint32_t stopLayerId, const sp<IRegionSamplingListener>& listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); - asBinder->linkToDeath(this); + asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); std::lock_guard lock(mSamplingMutex); - mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener}); + mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayerId, listener}); } void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) { @@ -203,25 +204,14 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st return 0.0f; } - // (b/133849373) ROT_90 screencap images produced upside down - auto area = sample_area; - if (orientation & ui::Transform::ROT_90) { - area.top = height - area.top; - area.bottom = height - area.bottom; - std::swap(area.top, area.bottom); - - area.left = width - area.left; - area.right = width - area.right; - std::swap(area.left, area.right); - } - - const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left); + const uint32_t pixelCount = + (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left); uint32_t accumulatedLuma = 0; // Calculates luma with approximation of Rec. 709 primaries - for (int32_t row = area.top; row < area.bottom; ++row) { + for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) { const uint32_t* rowBase = data + row * stride; - for (int32_t column = area.left; column < area.right; ++column) { + for (int32_t column = sample_area.left; column < sample_area.right; ++column) { uint32_t pixel = rowBase[column]; const uint32_t r = pixel & 0xFF; const uint32_t g = (pixel >> 8) & 0xFF; @@ -302,8 +292,8 @@ void RegionSamplingThread::captureSample() { 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()) { + for (const auto& [area, stopLayerId, listener] : descriptors) { + if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) { stopLayerFound = true; return; } @@ -344,8 +334,8 @@ void RegionSamplingThread::captureSample() { const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; sp<GraphicBuffer> graphicBuffer = - new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(), - PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread"); + sp<GraphicBuffer>::make(sampledBounds.getWidth(), sampledBounds.getHeight(), + PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread"); const status_t bufferStatus = graphicBuffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d", bufferStatus); diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 686b4b1e1f..b62b15cb6d 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -26,6 +26,7 @@ #include <chrono> #include <condition_variable> +#include <cstdint> #include <mutex> #include <thread> #include <unordered_map> @@ -73,7 +74,7 @@ public: // 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 wp<Layer>& stopLayer, + void addListener(const Rect& samplingArea, uint32_t stopLayerId, const sp<IRegionSamplingListener>& listener); // Remove the listener to stop receiving median luma notifications. void removeListener(const sp<IRegionSamplingListener>& listener); @@ -87,7 +88,7 @@ public: private: struct Descriptor { Rect area = Rect::EMPTY_RECT; - wp<Layer> stopLayer; + uint32_t stopLayerId; sp<IRegionSamplingListener> listener; }; diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp index 5de796d742..5bd8a99361 100644 --- a/services/surfaceflinger/Scheduler/Android.bp +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -18,6 +18,7 @@ cc_defaults { "libbase", "libcutils", "liblog", + "libui", "libutils", ], } @@ -39,6 +40,7 @@ cc_library_static { name: "libscheduler", defaults: ["libscheduler_defaults"], srcs: [ + "src/PresentLatencyTracker.cpp", "src/Timer.cpp", ], local_include_dirs: ["include"], @@ -50,6 +52,7 @@ cc_test { test_suites: ["device-tests"], defaults: ["libscheduler_defaults"], srcs: [ + "tests/PresentLatencyTrackerTest.cpp", "tests/TimerTest.cpp", ], static_libs: [ diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 639ba5a3f1..008d8c4461 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -124,12 +124,12 @@ DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t times return event; } -DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) { +DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) { DisplayEventReceiver::Event event; - event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(), - systemTime()}; - event.modeChange.modeId = mode->getId().value(); - event.modeChange.vsyncPeriod = mode->getVsyncPeriod(); + event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, + mode.modePtr->getPhysicalDisplayId(), systemTime()}; + event.modeChange.modeId = mode.modePtr->getId().value(); + event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs(); return event; } @@ -157,9 +157,9 @@ DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId di } // namespace -EventThreadConnection::EventThreadConnection( - EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration) +EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, + ResyncCallback resyncCallback, + EventRegistrationFlags eventRegistration) : resyncCallback(std::move(resyncCallback)), mOwnerUid(callingUid), mEventRegistration(eventRegistration), @@ -173,7 +173,7 @@ EventThreadConnection::~EventThreadConnection() { void EventThreadConnection::onFirstRef() { // NOTE: mEventThread doesn't hold a strong reference on us - mEventThread->registerDisplayEventConnection(this); + mEventThread->registerDisplayEventConnection(sp<EventThreadConnection>::fromExisting(this)); } binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) { @@ -188,20 +188,22 @@ binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChann } binder::Status EventThreadConnection::setVsyncRate(int rate) { - mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this); + mEventThread->setVsyncRate(static_cast<uint32_t>(rate), + sp<EventThreadConnection>::fromExisting(this)); return binder::Status::ok(); } binder::Status EventThreadConnection::requestNextVsync() { ATRACE_CALL(); - mEventThread->requestNextVsync(this); + mEventThread->requestNextVsync(sp<EventThreadConnection>::fromExisting(this)); return binder::Status::ok(); } binder::Status EventThreadConnection::getLatestVsyncEventData( ParcelableVsyncEventData* outVsyncEventData) { ATRACE_CALL(); - outVsyncEventData->vsync = mEventThread->getLatestVsyncEventData(this); + outVsyncEventData->vsync = + mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this)); return binder::Status::ok(); } @@ -235,16 +237,13 @@ namespace impl { EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, android::frametimeline::TokenManager* tokenManager, - InterceptVSyncsCallback interceptVSyncsCallback, ThrottleVsyncCallback throttleVsyncCallback, GetVsyncPeriodFunction getVsyncPeriodFunction) : mVSyncSource(std::move(vsyncSource)), mTokenManager(tokenManager), - mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)), mThrottleVsyncCallback(std::move(throttleVsyncCallback)), mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)), mThreadName(mVSyncSource->getName()) { - LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr, "getVsyncPeriodFunction must not be null"); @@ -288,11 +287,10 @@ void EventThread::setDuration(std::chrono::nanoseconds workDuration, } sp<EventThreadConnection> EventThread::createEventConnection( - ResyncCallback resyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration) const { - return new EventThreadConnection(const_cast<EventThread*>(this), - IPCThreadState::self()->getCallingUid(), - std::move(resyncCallback), eventRegistration); + ResyncCallback resyncCallback, EventRegistrationFlags eventRegistration) const { + return sp<EventThreadConnection>::make(const_cast<EventThread*>(this), + IPCThreadState::self()->getCallingUid(), + std::move(resyncCallback), eventRegistration); } status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) { @@ -407,7 +405,7 @@ void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) mCondition.notify_all(); } -void EventThread::onModeChanged(DisplayModePtr mode) { +void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) { std::lock_guard<std::mutex> lock(mMutex); mPendingEvents.push_back(makeModeChanged(mode)); @@ -442,21 +440,13 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { event = mPendingEvents.front(); mPendingEvents.pop_front(); - switch (event->header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - if (event->hotplug.connected && !mVSyncState) { - mVSyncState.emplace(event->header.displayId); - } else if (!event->hotplug.connected && mVSyncState && - mVSyncState->displayId == event->header.displayId) { - mVSyncState.reset(); - } - break; - - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - if (mInterceptVSyncsCallback) { - mInterceptVSyncsCallback(event->header.timestamp); - } - break; + if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) { + if (event->hotplug.connected && !mVSyncState) { + mVSyncState.emplace(event->header.displayId); + } else if (!event->hotplug.connected && mVSyncState && + mVSyncState->displayId == event->header.displayId) { + mVSyncState.reset(); + } } } @@ -553,7 +543,7 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { return connection->mEventRegistration.test( - ISurfaceComposer::EventRegistration::modeChanged); + gui::ISurfaceComposer::EventRegistration::modeChanged); } case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: @@ -586,7 +576,7 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, [[fallthrough]]; case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: return connection->mEventRegistration.test( - ISurfaceComposer::EventRegistration::frameRateOverride); + gui::ISurfaceComposer::EventRegistration::frameRateOverride); default: return false; @@ -678,6 +668,7 @@ void EventThread::dump(std::string& result) const { StringAppendF(&result, " %s\n", toString(*connection).c_str()); } } + result += '\n'; } const char* EventThread::toCString(State state) { diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index adb96fd462..43c359833e 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -23,6 +23,7 @@ #include <sys/types.h> #include <utils/Errors.h> +#include <scheduler/FrameRateMode.h> #include <condition_variable> #include <cstdint> #include <deque> @@ -92,7 +93,7 @@ public: class EventThreadConnection : public gui::BnDisplayEventConnection { public: EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); + EventRegistrationFlags eventRegistration = {}); virtual ~EventThreadConnection(); virtual status_t postEvent(const DisplayEventReceiver::Event& event); @@ -107,7 +108,7 @@ public: VSyncRequest vsyncRequest = VSyncRequest::None; const uid_t mOwnerUid; - const ISurfaceComposer::EventRegistrationFlags mEventRegistration; + const EventRegistrationFlags mEventRegistration; private: virtual void onFirstRef(); @@ -123,8 +124,7 @@ public: virtual ~EventThread(); virtual sp<EventThreadConnection> createEventConnection( - ResyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const = 0; + ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0; // called before the screen is turned off from main thread virtual void onScreenReleased() = 0; @@ -135,7 +135,7 @@ public: virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; // called when SF changes the active mode and apps needs to be notified about the change - virtual void onModeChanged(DisplayModePtr) = 0; + virtual void onModeChanged(const scheduler::FrameRateMode&) = 0; // called when SF updates the Frame Rate Override list virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId, @@ -162,17 +162,15 @@ namespace impl { class EventThread : public android::EventThread, private VSyncSource::Callback { public: - using InterceptVSyncsCallback = std::function<void(nsecs_t)>; using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>; using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>; - EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback, - ThrottleVsyncCallback, GetVsyncPeriodFunction); + EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, ThrottleVsyncCallback, + GetVsyncPeriodFunction); ~EventThread(); sp<EventThreadConnection> createEventConnection( - ResyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const override; + ResyncCallback, EventRegistrationFlags eventRegistration = {}) const override; status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override; void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override; @@ -188,7 +186,7 @@ public: void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; - void onModeChanged(DisplayModePtr) override; + void onModeChanged(const scheduler::FrameRateMode&) override; void onFrameRateOverridesChanged(PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; @@ -227,7 +225,6 @@ private: const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; - const InterceptVSyncsCallback mInterceptVSyncsCallback; const ThrottleVsyncCallback mThrottleVsyncCallback; const GetVsyncPeriodFunction mGetVsyncPeriodFunction; const char* const mThreadName; diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp index c233455994..cb9bfe93db 100644 --- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp +++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp @@ -54,10 +54,9 @@ std::optional<Fps> FrameRateOverrideMappings::getFrameRateOverrideForUid( std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides( bool supportsFrameRateOverrideByContent) { std::lock_guard lock(mFrameRateOverridesLock); + std::vector<FrameRateOverride> overrides; - overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(), - mFrameRateOverridesFromBackdoor.size(), - mFrameRateOverridesByContent.size()})); + overrides.reserve(maxOverridesCount()); for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()}); @@ -83,28 +82,34 @@ std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrid return overrides; } -void FrameRateOverrideMappings::dump(std::string& result) const { - using base::StringAppendF; +void FrameRateOverrideMappings::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; std::lock_guard lock(mFrameRateOverridesLock); - StringAppendF(&result, "Frame Rate Overrides (backdoor): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); - } - StringAppendF(&result, "}\n"); + const bool hasOverrides = maxOverridesCount() > 0; + dumper.dump("FrameRateOverrides"sv, hasOverrides ? ""sv : "none"sv); - StringAppendF(&result, "Frame Rate Overrides (GameManager): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); - } - StringAppendF(&result, "}\n"); + if (!hasOverrides) return; - StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); + dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent); + dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager); + dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor); +} + +void FrameRateOverrideMappings::dump(utils::Dumper& dumper, std::string_view name, + const UidToFrameRateOverride& overrides) const { + if (overrides.empty()) return; + + utils::Dumper::Indent indent(dumper); + dumper.dump(name); + { + utils::Dumper::Indent indent(dumper); + for (const auto& [uid, frameRate] : overrides) { + using namespace std::string_view_literals; + dumper.dump("(uid, frameRate)"sv, uid, frameRate); + } } - StringAppendF(&result, "}\n"); } bool FrameRateOverrideMappings::updateFrameRateOverridesByContent( diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h index 4185a4c0ab..da0f276a3b 100644 --- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h +++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h @@ -23,7 +23,10 @@ #include <map> #include <optional> +#include "Utils/Dumper.h" + namespace android::scheduler { + class FrameRateOverrideMappings { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; using UidToFrameRateOverride = std::map<uid_t, Fps>; @@ -34,7 +37,6 @@ public: EXCLUDES(mFrameRateOverridesLock); std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent) EXCLUDES(mFrameRateOverridesLock); - void dump(std::string& result) const; bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides) EXCLUDES(mFrameRateOverridesLock); void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) @@ -42,7 +44,17 @@ public: void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) EXCLUDES(mFrameRateOverridesLock); + void dump(utils::Dumper&) const; + private: + size_t maxOverridesCount() const REQUIRES(mFrameRateOverridesLock) { + return std::max({mFrameRateOverridesByContent.size(), + mFrameRateOverridesFromGameManager.size(), + mFrameRateOverridesFromBackdoor.size()}); + } + + void dump(utils::Dumper&, std::string_view name, const UidToFrameRateOverride&) const; + // The frame rate override lists need their own mutex as they are being read // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks mutable std::mutex mFrameRateOverridesLock; @@ -53,4 +65,5 @@ private: UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock); UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock); }; + } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h deleted file mode 100644 index 760a4ee886..0000000000 --- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 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 <mutex> - -#include "EventThread.h" - -namespace android { - -/** - * VSync signals used during SurfaceFlinger trace playback (traces we captured - * with SurfaceInterceptor). - */ -class InjectVSyncSource final : public VSyncSource { -public: - ~InjectVSyncSource() override = default; - - void setCallback(VSyncSource::Callback* callback) override { - std::lock_guard<std::mutex> lock(mCallbackMutex); - mCallback = callback; - } - - void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp, - nsecs_t deadlineTimestamp) { - std::lock_guard<std::mutex> lock(mCallbackMutex); - if (mCallback) { - mCallback->onVSyncEvent(when, {expectedVSyncTimestamp, deadlineTimestamp}); - } - } - - const char* getName() const override { return "inject"; } - void setVSyncEnabled(bool) override {} - void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {} - VSyncData getLatestVSyncData() const override { return {}; } - void dump(std::string&) const override {} - -private: - std::mutex mCallbackMutex; - VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; -}; - -} // namespace android diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 5f64efa3d1..7c9cedfab8 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -72,6 +72,20 @@ void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) { ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps); } + +LayerHistory::LayerVoteType getVoteType(LayerInfo::FrameRateCompatibility compatibility, + bool contentDetectionEnabled) { + LayerHistory::LayerVoteType voteType; + if (!contentDetectionEnabled || compatibility == LayerInfo::FrameRateCompatibility::NoVote) { + voteType = LayerHistory::LayerVoteType::NoVote; + } else if (compatibility == LayerInfo::FrameRateCompatibility::Min) { + voteType = LayerHistory::LayerVoteType::Min; + } else { + voteType = LayerHistory::LayerVoteType::Heuristic; + } + return voteType; +} + } // namespace LayerHistory::LayerHistory() @@ -81,10 +95,12 @@ LayerHistory::LayerHistory() LayerHistory::~LayerHistory() = default; -void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) { +void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) { std::lock_guard lock(mLock); LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound, "%s already registered", layer->getName().c_str()); + LayerVoteType type = + getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled); auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type); // The layer can be placed on either map, it is assumed that partitionLayers() will be called @@ -132,7 +148,23 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, } } -auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary { +void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) { + std::lock_guard lock(mLock); + auto id = layer->getSequence(); + + auto [found, layerPair] = findLayer(id); + if (found == LayerStatus::NotFound) { + // Offscreen layer + ALOGV("%s: %s not registered", __func__, layer->getName().c_str()); + return; + } + + const auto& info = layerPair->second; + info->setDefaultLayerVote( + getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled)); +} + +auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary { Summary summary; std::lock_guard lock(mLock); @@ -146,7 +178,7 @@ auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority, layerFocused ? "" : "not"); - const auto vote = info->getRefreshRateVote(configs, now); + const auto vote = info->getRefreshRateVote(selector, now); // Skip NoVote layer as those don't have any requirements if (vote.type == LayerVoteType::NoVote) { continue; @@ -203,6 +235,8 @@ void LayerHistory::partitionLayers(nsecs_t now) { switch (frameRate.type) { case Layer::FrameRateCompatibility::Default: return LayerVoteType::ExplicitDefault; + case Layer::FrameRateCompatibility::Min: + return LayerVoteType::Min; case Layer::FrameRateCompatibility::ExactOrMultiple: return LayerVoteType::ExplicitExactOrMultiple; case Layer::FrameRateCompatibility::NoVote: @@ -241,7 +275,7 @@ void LayerHistory::clear() { std::string LayerHistory::dump() const { std::lock_guard lock(mLock); - return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", + return base::StringPrintf("{size=%zu, active=%zu}", mActiveLayerInfos.size() + mInactiveLayerInfos.size(), mActiveLayerInfos.size()); } diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 7b6096f7f3..5022906ff9 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -27,7 +27,7 @@ #include <utility> #include <vector> -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" namespace android { @@ -39,13 +39,13 @@ class LayerInfo; class LayerHistory { public: - using LayerVoteType = RefreshRateConfigs::LayerVoteType; + using LayerVoteType = RefreshRateSelector::LayerVoteType; LayerHistory(); ~LayerHistory(); // Layers are unregistered when the weak reference expires. - void registerLayer(Layer*, LayerVoteType type); + void registerLayer(Layer*, bool contentDetectionEnabled); // Sets the display size. Client is responsible for synchronization. void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; } @@ -63,10 +63,14 @@ public: // Marks the layer as active, and records the given state to its history. void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType); - using Summary = std::vector<RefreshRateConfigs::LayerRequirement>; + // Updates the default frame rate compatibility which takes effect when the app + // does not set a preference for refresh rate. + void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled); + + using Summary = std::vector<RefreshRateSelector::LayerRequirement>; // Rebuilds sets of active/inactive layers, and accumulates stats for active layers. - Summary summarize(const RefreshRateConfigs&, nsecs_t now); + Summary summarize(const RefreshRateSelector&, nsecs_t now); void clear(); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 943615c45c..7247e4b260 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -187,8 +187,8 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { return static_cast<nsecs_t>(averageFrameTime); } -std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( - const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) { +std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector, + nsecs_t now) { static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); @@ -199,7 +199,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime); const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); if (refreshRateConsistent) { - const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate); + const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate); using fps_approx_ops::operator!=; // To avoid oscillation, use the last calculated refresh rate if it is close enough. @@ -222,7 +222,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( : std::nullopt; } -LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs, +LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector, nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); @@ -250,7 +250,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref clearHistory(now); } - auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now); + auto refreshRate = calculateRefreshRateIfPossible(selector, now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 8a3b0b97e4..a5ffbbecb5 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -28,7 +28,7 @@ #include <scheduler/Seamlessness.h> #include "LayerHistory.h" -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" namespace android { @@ -74,6 +74,8 @@ public: enum class FrameRateCompatibility { Default, // Layer didn't specify any specific handling strategy + Min, // Layer needs the minimum frame rate. + Exact, // Layer needs the exact frame rate. ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the @@ -160,7 +162,7 @@ public: uid_t getOwnerUid() const { return mOwnerUid; } - LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now); + LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now); // Return the last updated time. If the present time is farther in the future than the // updated time, the updated time is the present time. @@ -259,7 +261,7 @@ private: bool isFrequent(nsecs_t now) const; bool isAnimating(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; - std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now); + std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now); std::optional<nsecs_t> calculateAverageFrameTime() const; bool isFrameTimeValid(const FrameTimeData&) const; diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index f2af85e94a..ae10ff4989 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -30,11 +30,11 @@ namespace android::impl { -void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) { +void MessageQueue::Handler::dispatchFrame(VsyncId vsyncId, TimePoint expectedVsyncTime) { if (!mFramePending.exchange(true)) { mVsyncId = vsyncId; mExpectedVsyncTime = expectedVsyncTime; - mQueue.mLooper->sendMessage(this, Message()); + mQueue.mLooper->sendMessage(sp<MessageHandler>::fromExisting(this), Message()); } } @@ -44,16 +44,7 @@ bool MessageQueue::Handler::isFramePending() const { void MessageQueue::Handler::handleMessage(const Message&) { mFramePending.store(false); - - const nsecs_t frameTime = systemTime(); - auto& compositor = mQueue.mCompositor; - - if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) { - return; - } - - compositor.composite(frameTime, mVsyncId); - compositor.sample(); + mQueue.onFrameSignal(mQueue.mCompositor, mVsyncId, mExpectedVsyncTime); } MessageQueue::MessageQueue(ICompositor& compositor) @@ -66,52 +57,22 @@ MessageQueue::MessageQueue(ICompositor& compositor, sp<Handler> handler) mLooper(sp<Looper>::make(kAllowNonCallbacks)), mHandler(std::move(handler)) {} -// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly -// and remove the EventThread from MessageQueue -void MessageQueue::setInjector(sp<EventThreadConnection> connection) { - auto& tube = mInjector.tube; - - if (const int fd = tube.getFd(); fd >= 0) { - mLooper->removeFd(fd); - } - - if (connection) { - // The EventThreadConnection is retained when disabling injection, so avoid subsequently - // stealing invalid FDs. Note that the stolen FDs are kept open. - if (tube.getFd() < 0) { - connection->stealReceiveChannel(&tube); - } else { - ALOGW("Recycling channel for VSYNC injection."); - } - - mLooper->addFd( - tube.getFd(), 0, Looper::EVENT_INPUT, - [](int, int, void* data) { - reinterpret_cast<MessageQueue*>(data)->injectorCallback(); - return 1; // Keep registration. - }, - this); - } - - std::lock_guard lock(mInjector.mutex); - mInjector.connection = std::move(connection); -} - void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { ATRACE_CALL(); // Trace VSYNC-sf mVsync.value = (mVsync.value + 1) % 2; + const auto expectedVsyncTime = TimePoint::fromNs(vsyncTime); { std::lock_guard lock(mVsync.mutex); - mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime); + mVsync.lastCallbackTime = expectedVsyncTime; mVsync.scheduledFrameTime.reset(); } - const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions( - {targetWakeupTime, readyTime, vsyncTime}); + const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions( + {targetWakeupTime, readyTime, vsyncTime})}; - mHandler->dispatchFrame(vsyncId, vsyncTime); + mHandler->dispatchFrame(vsyncId, expectedVsyncTime); } void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, @@ -133,9 +94,10 @@ void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; if (mVsync.scheduledFrameTime) { - mVsync.scheduledFrameTime = mVsync.registration->schedule( - {mVsync.workDuration.get().count(), - /*readyDuration=*/0, mVsync.lastCallbackTime.count()}); + mVsync.scheduledFrameTime = + mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), + .readyDuration = 0, + .earliestVsync = mVsync.lastCallbackTime.ns()}); } } @@ -165,38 +127,27 @@ void MessageQueue::postMessage(sp<MessageHandler>&& handler) { mLooper->sendMessage(handler, Message()); } +void MessageQueue::scheduleConfigure() { + struct ConfigureHandler : MessageHandler { + explicit ConfigureHandler(ICompositor& compositor) : compositor(compositor) {} + + void handleMessage(const Message&) override { compositor.configure(); } + + ICompositor& compositor; + }; + + // TODO(b/241285876): Batch configure tasks that happen within some duration. + postMessage(sp<ConfigureHandler>::make(mCompositor)); +} + void MessageQueue::scheduleFrame() { ATRACE_CALL(); - { - std::lock_guard lock(mInjector.mutex); - if (CC_UNLIKELY(mInjector.connection)) { - ALOGD("%s while injecting VSYNC", __FUNCTION__); - mInjector.connection->requestNextVsync(); - return; - } - } - std::lock_guard lock(mVsync.mutex); mVsync.scheduledFrameTime = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.count()}); -} - -void MessageQueue::injectorCallback() { - ssize_t n; - DisplayEventReceiver::Event buffer[8]; - while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) { - for (int i = 0; i < n; i++) { - if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - auto& vsync = buffer[i].vsync; - mHandler->dispatchFrame(vsync.vsyncData.preferredVsyncId(), - vsync.vsyncData.preferredExpectedPresentationTime()); - break; - } - } - } + .earliestVsync = mVsync.lastCallbackTime.ns()}); } auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> { diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 4082e26874..04de492479 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -25,8 +25,12 @@ #include <android/gui/IDisplayEventConnection.h> #include <private/gui/BitTube.h> #include <utils/Looper.h> +#include <utils/StrongPointer.h> #include <utils/Timers.h> +#include <scheduler/Time.h> +#include <scheduler/VsyncId.h> + #include "EventThread.h" #include "TracedOrdinal.h" #include "VSyncDispatch.h" @@ -34,8 +38,9 @@ namespace android { struct ICompositor { - virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0; - virtual void composite(nsecs_t frameTime, int64_t vsyncId) = 0; + virtual void configure() = 0; + virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0; + virtual void composite(TimePoint frameTime, VsyncId) = 0; virtual void sample() = 0; protected: @@ -47,6 +52,9 @@ class Task : public MessageHandler { template <typename G> friend auto makeTask(G&&); + template <typename... Args> + friend sp<Task<F>> sp<Task<F>>::make(Args&&... args); + explicit Task(F&& f) : mTask(std::move(f)) {} void handleMessage(const Message&) override { mTask(); } @@ -57,7 +65,7 @@ class Task : public MessageHandler { template <typename F> inline auto makeTask(F&& f) { - sp<Task<F>> task = new Task<F>(std::move(f)); + sp<Task<F>> task = sp<Task<F>>::make(std::forward<F>(f)); return std::make_pair(task, task->mTask.get_future()); } @@ -68,9 +76,9 @@ public: virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) = 0; virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; - virtual void setInjector(sp<EventThreadConnection>) = 0; virtual void waitMessage() = 0; virtual void postMessage(sp<MessageHandler>&&) = 0; + virtual void scheduleConfigure() = 0; virtual void scheduleFrame() = 0; using Clock = std::chrono::steady_clock; @@ -84,8 +92,9 @@ protected: class Handler : public MessageHandler { MessageQueue& mQueue; std::atomic_bool mFramePending = false; - std::atomic<int64_t> mVsyncId = 0; - std::atomic<nsecs_t> mExpectedVsyncTime = 0; + + std::atomic<VsyncId> mVsyncId; + std::atomic<TimePoint> mExpectedVsyncTime; public: explicit Handler(MessageQueue& queue) : mQueue(queue) {} @@ -93,7 +102,7 @@ protected: bool isFramePending() const; - virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime); + virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime); }; friend class Handler; @@ -104,6 +113,8 @@ protected: void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime); private: + virtual void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) = 0; + ICompositor& mCompositor; const sp<Looper> mLooper; const sp<Handler> mHandler; @@ -115,21 +126,12 @@ private: mutable std::mutex mutex; TracedOrdinal<std::chrono::nanoseconds> workDuration GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)}; - std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0}; + TimePoint lastCallbackTime GUARDED_BY(mutex); std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex); TracedOrdinal<int> value = {"VSYNC-sf", 0}; }; - struct Injector { - gui::BitTube tube; - std::mutex mutex; - sp<EventThreadConnection> connection GUARDED_BY(mutex); - }; - Vsync mVsync; - Injector mInjector; - - void injectorCallback(); public: explicit MessageQueue(ICompositor&); @@ -137,11 +139,11 @@ public: void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) override; void setDuration(std::chrono::nanoseconds workDuration) override; - void setInjector(sp<EventThreadConnection>) override; void waitMessage() override; void postMessage(sp<MessageHandler>&&) override; + void scheduleConfigure() override; void scheduleFrame() override; std::optional<Clock::time_point> getScheduledFrameTime() const override; diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index 3c8dc64f10..cd45bfdab3 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -179,11 +179,5 @@ void OneShotTimer::reset() { } } -std::string OneShotTimer::dump() const { - std::ostringstream stream; - stream << mInterval.count() << " ms"; - return stream.str(); -} - } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index 2017c31513..f95646c48f 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -23,6 +23,7 @@ #include "../Clock.h" #include <android-base/thread_annotations.h> +#include <scheduler/Time.h> namespace android { namespace scheduler { @@ -42,6 +43,8 @@ public: std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); ~OneShotTimer(); + Duration interval() const { return mInterval; } + // Initializes and turns on the idle timer. void start(); // Stops the idle timer and any held resources. @@ -49,8 +52,6 @@ public: // Resets the wakeup time and fires the reset callback. void reset(); - std::string dump() const; - private: // Enum to track in what state is the timer. enum class TimerState { diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp deleted file mode 100644 index a48c921378..0000000000 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// #define LOG_NDEBUG 0 -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra" - -#include <chrono> -#include <cmath> - -#include <android-base/properties.h> -#include <android-base/stringprintf.h> -#include <ftl/enum.h> -#include <utils/Trace.h> - -#include "../SurfaceFlingerProperties.h" -#include "RefreshRateConfigs.h" - -#undef LOG_TAG -#define LOG_TAG "RefreshRateConfigs" - -namespace android::scheduler { -namespace { - -struct RefreshRateScore { - DisplayModeIterator modeIt; - float overallScore; - struct { - float modeBelowThreshold; - float modeAboveThreshold; - } fixedRateBelowThresholdLayersScore; -}; - -template <typename Iterator> -const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) { - const auto it = - std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) { - const auto& [modeIt, overallScore, _] = current; - - std::string name = to_string(modeIt->second->getFps()); - ALOGV("%s scores %.2f", name.c_str(), overallScore); - - ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100))); - - constexpr float kEpsilon = 0.0001f; - return overallScore > max.overallScore * (1 + kEpsilon); - }); - - return it->modeIt->second; -} - -constexpr RefreshRateConfigs::GlobalSignals kNoSignals; - -std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) { - return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(), - ftl::enum_string(layer.vote).c_str(), weight, - ftl::enum_string(layer.seamlessness).c_str(), - to_string(layer.desiredRefreshRate).c_str()); -} - -std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) { - std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz}; - knownFrameRates.reserve(knownFrameRates.size() + modes.size()); - - // Add all supported refresh rates. - for (const auto& [id, mode] : modes) { - knownFrameRates.push_back(mode->getFps()); - } - - // Sort and remove duplicates. - std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess); - knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), - isApproxEqual), - knownFrameRates.end()); - return knownFrameRates; -} - -// The Filter is a `bool(const DisplayMode&)` predicate. -template <typename Filter> -std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) { - std::vector<DisplayModeIterator> sortedModes; - sortedModes.reserve(modes.size()); - - for (auto it = modes.begin(); it != modes.end(); ++it) { - const auto& [id, mode] = *it; - - if (filter(*mode)) { - ALOGV("%s: including mode %d", __func__, id.value()); - sortedModes.push_back(it); - } - } - - std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) { - const auto& mode1 = it1->second; - const auto& mode2 = it2->second; - - if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) { - return mode1->getGroup() > mode2->getGroup(); - } - - return mode1->getVsyncPeriod() > mode2->getVsyncPeriod(); - }); - - return sortedModes; -} - -bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) { - for (const auto it1 : sortedModes) { - const auto& mode1 = it1->second; - for (const auto it2 : sortedModes) { - const auto& mode2 = it2->second; - - if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) { - return true; - } - } - } - return false; -} - -} // namespace - -std::string RefreshRateConfigs::Policy::toString() const { - return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" - ", primaryRange=%s, appRequestRange=%s}", - defaultMode.value(), allowGroupSwitching ? "true" : "false", - to_string(primaryRange).c_str(), to_string(appRequestRange).c_str()); -} - -std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, - nsecs_t displayPeriod) const { - auto [quotient, remainder] = std::div(layerPeriod, displayPeriod); - if (remainder <= MARGIN_FOR_PERIOD_CALCULATION || - std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { - quotient++; - remainder = 0; - } - - return {quotient, remainder}; -} - -float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer, - Fps refreshRate) const { - constexpr float kScoreForFractionalPairs = .8f; - - const auto displayPeriod = refreshRate.getPeriodNsecs(); - const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs(); - if (layer.vote == LayerVoteType::ExplicitDefault) { - // Find the actual rate the layer will render, assuming - // that layerPeriod is the minimal period to render a frame. - // For example if layerPeriod is 20ms and displayPeriod is 16ms, - // then the actualLayerPeriod will be 32ms, because it is the - // smallest multiple of the display period which is >= layerPeriod. - auto actualLayerPeriod = displayPeriod; - int multiplier = 1; - while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) { - multiplier++; - actualLayerPeriod = displayPeriod * multiplier; - } - - // Because of the threshold we used above it's possible that score is slightly - // above 1. - return std::min(1.0f, - static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod)); - } - - if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || - layer.vote == LayerVoteType::Heuristic) { - if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) { - return kScoreForFractionalPairs; - } - - // Calculate how many display vsyncs we need to present a single frame for this - // layer - const auto [displayFramesQuotient, displayFramesRemainder] = - getDisplayFrames(layerPeriod, displayPeriod); - static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 - if (displayFramesRemainder == 0) { - // Layer desired refresh rate matches the display rate. - return 1.0f; - } - - if (displayFramesQuotient == 0) { - // Layer desired refresh rate is higher than the display rate. - return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * - (1.0f / (MAX_FRAMES_TO_FIT + 1)); - } - - // Layer desired refresh rate is lower than the display rate. Check how well it fits - // the cadence. - auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder)); - int iter = 2; - while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { - diff = diff - (displayPeriod - diff); - iter++; - } - - return (1.0f / iter); - } - - return 0; -} - -float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, - bool isSeamlessSwitch) const { - // Slightly prefer seamless switches. - constexpr float kSeamedSwitchPenalty = 0.95f; - const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; - - // If the layer wants Max, give higher score to the higher refresh rate - if (layer.vote == LayerVoteType::Max) { - const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second; - const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue(); - // use ratio^2 to get a lower score the more we get further from peak - return ratio * ratio; - } - - if (layer.vote == LayerVoteType::ExplicitExact) { - const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate); - if (mSupportsFrameRateOverrideByContent) { - // Since we support frame rate override, allow refresh rates which are - // multiples of the layer's request, as those apps would be throttled - // down to run at the desired refresh rate. - return divisor > 0; - } - - return divisor == 1; - } - - // If the layer frame rate is a divisor of the refresh rate it should score - // the highest score. - if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) { - return 1.0f * seamlessness; - } - - // The layer frame rate is not a divisor of the refresh rate, - // there is a small penalty attached to the score to favor the frame rates - // the exactly matches the display refresh rate or a multiple. - constexpr float kNonExactMatchingPenalty = 0.95f; - return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness * - kNonExactMatchingPenalty; -} - -auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const - -> std::pair<DisplayModePtr, GlobalSignals> { - std::lock_guard lock(mLock); - - if (mGetBestRefreshRateCache && - mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) { - return mGetBestRefreshRateCache->result; - } - - const auto result = getBestRefreshRateLocked(layers, signals); - mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result}; - return result; -} - -auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const - -> std::pair<DisplayModePtr, GlobalSignals> { - using namespace fps_approx_ops; - ATRACE_CALL(); - ALOGV("%s: %zu layers", __func__, layers.size()); - - int noVoteLayers = 0; - int minVoteLayers = 0; - int maxVoteLayers = 0; - int explicitDefaultVoteLayers = 0; - int explicitExactOrMultipleVoteLayers = 0; - int explicitExact = 0; - float maxExplicitWeight = 0; - int seamedFocusedLayers = 0; - - for (const auto& layer : layers) { - switch (layer.vote) { - case LayerVoteType::NoVote: - noVoteLayers++; - break; - case LayerVoteType::Min: - minVoteLayers++; - break; - case LayerVoteType::Max: - maxVoteLayers++; - break; - case LayerVoteType::ExplicitDefault: - explicitDefaultVoteLayers++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::ExplicitExactOrMultiple: - explicitExactOrMultipleVoteLayers++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::ExplicitExact: - explicitExact++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::Heuristic: - break; - } - - if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) { - seamedFocusedLayers++; - } - } - - const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 || - explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0; - - const Policy* policy = getCurrentPolicyLocked(); - const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get(); - // If the default mode group is different from the group of current mode, - // this means a layer requesting a seamed mode switch just disappeared and - // we should switch back to the default group. - // However if a seamed layer is still present we anchor around the group - // of the current mode, in order to prevent unnecessary seamed mode switches - // (e.g. when pausing a video playback). - const auto anchorGroup = - seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup(); - - // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've - // selected a refresh rate to see if we should apply touch boost. - if (signals.touch && !hasExplicitVoteLayers) { - const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup); - ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str()); - return {max, GlobalSignals{.touch = true}}; - } - - // If the primary range consists of a single refresh rate then we can only - // move out the of range if layers explicitly request a different refresh - // rate. - const bool primaryRangeIsSingleRate = - isApproxEqual(policy->primaryRange.min, policy->primaryRange.max); - - if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { - const DisplayModePtr& min = getMinRefreshRateByPolicyLocked(); - ALOGV("Idle - choose %s", to_string(min->getFps()).c_str()); - return {min, GlobalSignals{.idle = true}}; - } - - if (layers.empty() || noVoteLayers == layers.size()) { - const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup); - ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str()); - return {max, kNoSignals}; - } - - // Only if all layers want Min we should return Min - if (noVoteLayers + minVoteLayers == layers.size()) { - const DisplayModePtr& min = getMinRefreshRateByPolicyLocked(); - ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str()); - return {min, kNoSignals}; - } - - // Find the best refresh rate based on score - std::vector<RefreshRateScore> scores; - scores.reserve(mAppRequestRefreshRates.size()); - - for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) { - scores.emplace_back(RefreshRateScore{modeIt, 0.0f}); - } - - for (const auto& layer : layers) { - ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(), - ftl::enum_string(layer.vote).c_str(), layer.weight, - layer.desiredRefreshRate.getValue()); - if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { - continue; - } - - const auto weight = layer.weight; - - for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { - const auto& [id, mode] = *modeIt; - const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup(); - - if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) { - ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s", - formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(), - to_string(*mActiveModeIt->second).c_str()); - continue; - } - - if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch && - !layer.focused) { - ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed." - " Current mode = %s", - formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(), - to_string(*mActiveModeIt->second).c_str()); - continue; - } - - // Layers with default seamlessness vote for the current mode group if - // there are layers with seamlessness=SeamedAndSeamless and for the default - // mode group otherwise. In second case, if the current mode group is different - // from the default, this means a layer with seamlessness=SeamedAndSeamless has just - // disappeared. - const bool isInPolicyForDefault = mode->getGroup() == anchorGroup; - if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { - ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), - to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str()); - continue; - } - - const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps()); - if ((primaryRangeIsSingleRate || !inPrimaryRange) && - !(layer.focused && - (layer.vote == LayerVoteType::ExplicitDefault || - layer.vote == LayerVoteType::ExplicitExact))) { - // Only focused layers with ExplicitDefault frame rate settings are allowed to score - // refresh rates outside the primary range. - continue; - } - - const float layerScore = - calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch); - const float weightedLayerScore = weight * layerScore; - - // Layer with fixed source has a special consideration which depends on the - // mConfig.frameRateMultipleThreshold. We don't want these layers to score - // refresh rates above the threshold, but we also don't want to favor the lower - // ones by having a greater number of layers scoring them. Instead, we calculate - // the score independently for these layers and later decide which - // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not - // score 120 Hz, but desired 60 fps should contribute to the score. - const bool fixedSourceLayer = [](LayerVoteType vote) { - switch (vote) { - case LayerVoteType::ExplicitExactOrMultiple: - case LayerVoteType::Heuristic: - return true; - case LayerVoteType::NoVote: - case LayerVoteType::Min: - case LayerVoteType::Max: - case LayerVoteType::ExplicitDefault: - case LayerVoteType::ExplicitExact: - return false; - } - }(layer.vote); - const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 && - layer.desiredRefreshRate < - Fps::fromValue(mConfig.frameRateMultipleThreshold / 2); - if (fixedSourceLayer && layerBelowThreshold) { - const bool modeAboveThreshold = - mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); - if (modeAboveThreshold) { - ALOGV("%s gives %s fixed source (above threshold) score of %.4f", - formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), - layerScore); - fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore; - } else { - ALOGV("%s gives %s fixed source (below threshold) score of %.4f", - formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), - layerScore); - fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore; - } - } else { - ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(), - to_string(mode->getFps()).c_str(), layerScore); - overallScore += weightedLayerScore; - } - } - } - - // We want to find the best refresh rate without the fixed source layers, - // so we could know whether we should add the modeAboveThreshold scores or not. - // If the best refresh rate is already above the threshold, it means that - // some non-fixed source layers already scored it, so we can just add the score - // for all fixed source layers, even the ones that are above the threshold. - const bool maxScoreAboveThreshold = [&] { - if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) { - return false; - } - - const auto maxScoreIt = - std::max_element(scores.begin(), scores.end(), - [](RefreshRateScore max, RefreshRateScore current) { - const auto& [modeIt, overallScore, _] = current; - return overallScore > max.overallScore; - }); - ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for " - "refresh rate multiples", - to_string(maxScoreIt->modeIt->second->getFps()).c_str(), - maxScoreAboveThreshold ? "above" : "below"); - return maxScoreIt->modeIt->second->getFps() >= - Fps::fromValue(mConfig.frameRateMultipleThreshold); - }(); - - // Now we can add the fixed rate layers score - for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { - overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold; - if (maxScoreAboveThreshold) { - overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold; - } - ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(), - overallScore); - } - - // Now that we scored all the refresh rates we need to pick the one that got the highest - // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers - // wanted Max, or the lower otherwise. - const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0 - ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend()) - : getMaxScoreRefreshRate(scores.begin(), scores.end()); - - if (primaryRangeIsSingleRate) { - // If we never scored any layers, then choose the rate from the primary - // range instead of picking a random score from the app range. - if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.overallScore == 0; })) { - const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup); - ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str()); - return {max, kNoSignals}; - } else { - return {bestRefreshRate, kNoSignals}; - } - } - - // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly - // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit - // vote we should not change it if we get a touch event. Only apply touch boost if it will - // actually increase the refresh rate over the normal selection. - const DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); - - const bool touchBoostForExplicitExact = [&] { - if (mSupportsFrameRateOverrideByContent) { - // Enable touch boost if there are other layers besides exact - return explicitExact + noVoteLayers != layers.size(); - } else { - // Enable touch boost if there are no exact layers - return explicitExact == 0; - } - }(); - - using fps_approx_ops::operator<; - - if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && - bestRefreshRate->getFps() < touchRefreshRate->getFps()) { - ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str()); - return {touchRefreshRate, GlobalSignals{.touch = true}}; - } - - return {bestRefreshRate, kNoSignals}; -} - -std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> -groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) { - std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid; - for (const auto& layer : layers) { - auto iter = layersByUid.emplace(layer.ownerUid, - std::vector<const RefreshRateConfigs::LayerRequirement*>()); - auto& layersWithSameUid = iter.first->second; - layersWithSameUid.push_back(&layer); - } - - // Remove uids that can't have a frame rate override - for (auto iter = layersByUid.begin(); iter != layersByUid.end();) { - const auto& layersWithSameUid = iter->second; - bool skipUid = false; - for (const auto& layer : layersWithSameUid) { - if (layer->vote == RefreshRateConfigs::LayerVoteType::Max || - layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) { - skipUid = true; - break; - } - } - if (skipUid) { - iter = layersByUid.erase(iter); - } else { - ++iter; - } - } - - return layersByUid; -} - -RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides( - const std::vector<LayerRequirement>& layers, Fps displayRefreshRate, - GlobalSignals globalSignals) const { - ATRACE_CALL(); - - ALOGV("%s: %zu layers", __func__, layers.size()); - - std::lock_guard lock(mLock); - - std::vector<RefreshRateScore> scores; - scores.reserve(mDisplayModes.size()); - - for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) { - scores.emplace_back(RefreshRateScore{it, 0.0f}); - } - - std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) { - const auto& mode1 = lhs.modeIt->second; - const auto& mode2 = rhs.modeIt->second; - return isStrictlyLess(mode1->getFps(), mode2->getFps()); - }); - - std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid = - groupLayersByUid(layers); - UidToFrameRateOverride frameRateOverrides; - for (const auto& [uid, layersWithSameUid] : layersByUid) { - // Layers with ExplicitExactOrMultiple expect touch boost - const bool hasExplicitExactOrMultiple = - std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(), - [](const auto& layer) { - return layer->vote == LayerVoteType::ExplicitExactOrMultiple; - }); - - if (globalSignals.touch && hasExplicitExactOrMultiple) { - continue; - } - - for (auto& [_, score, _1] : scores) { - score = 0; - } - - for (const auto& layer : layersWithSameUid) { - if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) { - continue; - } - - LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && - layer->vote != LayerVoteType::ExplicitExactOrMultiple && - layer->vote != LayerVoteType::ExplicitExact); - for (auto& [modeIt, score, _] : scores) { - constexpr bool isSeamlessSwitch = true; - const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(), - isSeamlessSwitch); - score += layer->weight * layerScore; - } - } - - // We just care about the refresh rates which are a divisor of the - // display refresh rate - const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) { - const auto& [id, mode] = *score.modeIt; - return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0; - }); - scores.erase(it, scores.end()); - - // If we never scored any layers, we don't have a preferred frame rate - if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.overallScore == 0; })) { - continue; - } - - // Now that we scored all the refresh rates we need to pick the one that got the highest - // score. - const DisplayModePtr& bestRefreshRate = - getMaxScoreRefreshRate(scores.begin(), scores.end()); - - frameRateOverrides.emplace(uid, bestRefreshRate->getFps()); - } - - return frameRateOverrides; -} - -std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged( - std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const { - std::lock_guard lock(mLock); - - const DisplayModePtr& current = desiredActiveModeId - ? mDisplayModes.get(*desiredActiveModeId)->get() - : mActiveModeIt->second; - - const DisplayModePtr& min = mMinRefreshRateModeIt->second; - if (current == min) { - return {}; - } - - const auto& mode = timerExpired ? min : current; - return mode->getFps(); -} - -const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { - for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) { - const auto& mode = modeIt->second; - if (mActiveModeIt->second->getGroup() == mode->getGroup()) { - return mode; - } - } - - ALOGE("Can't find min refresh rate by policy with the same mode group" - " as the current mode %s", - to_string(*mActiveModeIt->second).c_str()); - - // Default to the lowest refresh rate. - return mPrimaryRefreshRates.front()->second; -} - -DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const { - std::lock_guard lock(mLock); - return getMaxRefreshRateByPolicyLocked(); -} - -const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { - for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) { - const auto& mode = (*it)->second; - if (anchorGroup == mode->getGroup()) { - return mode; - } - } - - ALOGE("Can't find max refresh rate by policy with the same mode group" - " as the current mode %s", - to_string(*mActiveModeIt->second).c_str()); - - // Default to the highest refresh rate. - return mPrimaryRefreshRates.back()->second; -} - -DisplayModePtr RefreshRateConfigs::getActiveMode() const { - std::lock_guard lock(mLock); - return mActiveModeIt->second; -} - -void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) { - std::lock_guard lock(mLock); - - // Invalidate the cached invocation to getBestRefreshRate. This forces - // the refresh rate to be recomputed on the next call to getBestRefreshRate. - mGetBestRefreshRateCache.reset(); - - mActiveModeIt = mDisplayModes.find(modeId); - LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end()); -} - -RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId, - Config config) - : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { - initializeIdleTimer(); - updateDisplayModes(std::move(modes), activeModeId); -} - -void RefreshRateConfigs::initializeIdleTimer() { - if (mConfig.idleTimerTimeout > 0ms) { - mIdleTimer.emplace( - "IdleTimer", mConfig.idleTimerTimeout, - [this] { - std::scoped_lock lock(mIdleTimerCallbacksMutex); - if (const auto callbacks = getIdleTimerCallbacks()) { - callbacks->onReset(); - } - }, - [this] { - std::scoped_lock lock(mIdleTimerCallbacksMutex); - if (const auto callbacks = getIdleTimerCallbacks()) { - callbacks->onExpired(); - } - }); - } -} - -void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { - std::lock_guard lock(mLock); - - // Invalidate the cached invocation to getBestRefreshRate. This forces - // the refresh rate to be recomputed on the next call to getBestRefreshRate. - mGetBestRefreshRateCache.reset(); - - mDisplayModes = std::move(modes); - mActiveModeIt = mDisplayModes.find(activeModeId); - LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end()); - - const auto sortedModes = - sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; }); - mMinRefreshRateModeIt = sortedModes.front(); - mMaxRefreshRateModeIt = sortedModes.back(); - - // Reset the policy because the old one may no longer be valid. - mDisplayManagerPolicy = {}; - mDisplayManagerPolicy.defaultMode = activeModeId; - - mSupportsFrameRateOverrideByContent = - mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes); - - constructAvailableRefreshRates(); -} - -bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const { - // defaultMode must be a valid mode, and within the given refresh rate range. - if (const auto mode = mDisplayModes.get(policy.defaultMode)) { - if (!policy.primaryRange.includes(mode->get()->getFps())) { - ALOGE("Default mode is not in the primary range."); - return false; - } - } else { - ALOGE("Default mode is not found."); - return false; - } - - using namespace fps_approx_ops; - return policy.appRequestRange.min <= policy.primaryRange.min && - policy.appRequestRange.max >= policy.primaryRange.max; -} - -status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { - std::lock_guard lock(mLock); - if (!isPolicyValidLocked(policy)) { - ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str()); - return BAD_VALUE; - } - mGetBestRefreshRateCache.reset(); - Policy previousPolicy = *getCurrentPolicyLocked(); - mDisplayManagerPolicy = policy; - if (*getCurrentPolicyLocked() == previousPolicy) { - return CURRENT_POLICY_UNCHANGED; - } - constructAvailableRefreshRates(); - return NO_ERROR; -} - -status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) { - std::lock_guard lock(mLock); - if (policy && !isPolicyValidLocked(*policy)) { - return BAD_VALUE; - } - mGetBestRefreshRateCache.reset(); - Policy previousPolicy = *getCurrentPolicyLocked(); - mOverridePolicy = policy; - if (*getCurrentPolicyLocked() == previousPolicy) { - return CURRENT_POLICY_UNCHANGED; - } - constructAvailableRefreshRates(); - return NO_ERROR; -} - -const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const { - return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy; -} - -RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const { - std::lock_guard lock(mLock); - return *getCurrentPolicyLocked(); -} - -RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const { - std::lock_guard lock(mLock); - return mDisplayManagerPolicy; -} - -bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const { - std::lock_guard lock(mLock); - return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(), - [modeId](DisplayModeIterator modeIt) { - return modeIt->second->getId() == modeId; - }); -} - -void RefreshRateConfigs::constructAvailableRefreshRates() { - // Filter modes based on current policy and sort on refresh rate. - const Policy* policy = getCurrentPolicyLocked(); - ALOGV("%s: %s ", __func__, policy->toString().c_str()); - - const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get(); - - const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) { - const auto filter = [&](const DisplayMode& mode) { - return mode.getResolution() == defaultMode->getResolution() && - mode.getDpi() == defaultMode->getDpi() && - (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) && - range.includes(mode.getFps()); - }; - - const auto modes = sortByRefreshRate(mDisplayModes, filter); - LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName, - to_string(range).c_str()); - - const auto stringifyModes = [&] { - std::string str; - for (const auto modeIt : modes) { - str += to_string(modeIt->second->getFps()); - str.push_back(' '); - } - return str; - }; - ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str()); - - return modes; - }; - - mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary"); - mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request"); -} - -Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { - using namespace fps_approx_ops; - - if (frameRate <= mKnownFrameRates.front()) { - return mKnownFrameRates.front(); - } - - if (frameRate >= mKnownFrameRates.back()) { - return mKnownFrameRates.back(); - } - - auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate, - isStrictlyLess); - - const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue()); - const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue()); - return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); -} - -RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const { - std::lock_guard lock(mLock); - - const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps(); - const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked(); - - // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that - // the min allowed refresh rate is higher than the device min, we do not want to enable the - // timer. - if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) { - return KernelIdleTimerAction::TurnOff; - } - - const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked(); - if (minByPolicy == maxByPolicy) { - // Turn on the timer when the min of the primary range is below the device min. - if (const Policy* currentPolicy = getCurrentPolicyLocked(); - isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) { - return KernelIdleTimerAction::TurnOn; - } - return KernelIdleTimerAction::TurnOff; - } - - // Turn on the timer in all other cases. - return KernelIdleTimerAction::TurnOn; -} - -int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) { - // This calculation needs to be in sync with the java code - // in DisplayManagerService.getDisplayInfoForFrameRateOverride - - // The threshold must be smaller than 0.001 in order to differentiate - // between the fractional pairs (e.g. 59.94 and 60). - constexpr float kThreshold = 0.0009f; - const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue(); - const auto numPeriodsRounded = std::round(numPeriods); - if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { - return 0; - } - - return static_cast<int>(numPeriodsRounded); -} - -bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { - if (isStrictlyLess(bigger, smaller)) { - return isFractionalPairOrMultiple(bigger, smaller); - } - - const auto multiplier = std::round(bigger.getValue() / smaller.getValue()); - constexpr float kCoef = 1000.f / 1001.f; - return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) || - isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef)); -} - -void RefreshRateConfigs::dump(std::string& result) const { - using namespace std::string_literals; - - std::lock_guard lock(mLock); - - const auto activeModeId = mActiveModeIt->first; - result += " activeModeId="s; - result += std::to_string(activeModeId.value()); - - result += "\n displayModes=\n"s; - for (const auto& [id, mode] : mDisplayModes) { - result += " "s; - result += to_string(*mode); - result += '\n'; - } - - base::StringAppendF(&result, " displayManagerPolicy=%s\n", - mDisplayManagerPolicy.toString().c_str()); - - if (const Policy& currentPolicy = *getCurrentPolicyLocked(); - mOverridePolicy && currentPolicy != mDisplayManagerPolicy) { - base::StringAppendF(&result, " overridePolicy=%s\n", currentPolicy.toString().c_str()); - } - - base::StringAppendF(&result, " supportsFrameRateOverrideByContent=%s\n", - mSupportsFrameRateOverrideByContent ? "true" : "false"); - - result += " idleTimer="s; - if (mIdleTimer) { - result += mIdleTimer->dump(); - } else { - result += "off"s; - } - - if (const auto controller = mConfig.kernelIdleTimerController) { - base::StringAppendF(&result, " (kernel via %s)", ftl::enum_string(*controller).c_str()); - } else { - result += " (platform)"s; - } - - result += '\n'; -} - -std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() { - return mConfig.idleTimerTimeout; -} - -} // namespace android::scheduler - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp new file mode 100644 index 0000000000..a05d3df2de --- /dev/null +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -0,0 +1,1304 @@ +/* + * 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 + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextra" + +#include <chrono> +#include <cmath> +#include <deque> +#include <map> + +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <ftl/enum.h> +#include <ftl/fake_guard.h> +#include <ftl/match.h> +#include <ftl/unit.h> +#include <scheduler/FrameRateMode.h> +#include <utils/Trace.h> + +#include "../SurfaceFlingerProperties.h" +#include "RefreshRateSelector.h" + +#undef LOG_TAG +#define LOG_TAG "RefreshRateSelector" + +namespace android::scheduler { +namespace { + +struct RefreshRateScore { + FrameRateMode frameRateMode; + float overallScore; + struct { + float modeBelowThreshold; + float modeAboveThreshold; + } fixedRateBelowThresholdLayersScore; +}; + +constexpr RefreshRateSelector::GlobalSignals kNoSignals; + +std::string formatLayerInfo(const RefreshRateSelector::LayerRequirement& layer, float weight) { + return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(), + ftl::enum_string(layer.vote).c_str(), weight, + ftl::enum_string(layer.seamlessness).c_str(), + to_string(layer.desiredRefreshRate).c_str()); +} + +std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) { + std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz}; + knownFrameRates.reserve(knownFrameRates.size() + modes.size()); + + // Add all supported refresh rates. + for (const auto& [id, mode] : modes) { + knownFrameRates.push_back(mode->getFps()); + } + + // Sort and remove duplicates. + std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess); + knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), + isApproxEqual), + knownFrameRates.end()); + return knownFrameRates; +} + +std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) { + std::vector<DisplayModeIterator> sortedModes; + sortedModes.reserve(modes.size()); + for (auto it = modes.begin(); it != modes.end(); ++it) { + sortedModes.push_back(it); + } + + std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) { + const auto& mode1 = it1->second; + const auto& mode2 = it2->second; + + if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) { + return mode1->getGroup() > mode2->getGroup(); + } + + return mode1->getVsyncPeriod() > mode2->getVsyncPeriod(); + }); + + return sortedModes; +} + +std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range, + RefreshRateSelector::Config::FrameRateOverride config) { + if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) { + return {1, 1}; + } + + using fps_approx_ops::operator/; + // use signed type as `fps / range.max` might be 0 + const auto start = std::max(1, static_cast<int>(fps / range.max) - 1); + const auto end = fps / + std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate, + fps_approx_ops::operator<); + + return {start, end}; +} + +bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) { + for (const auto it1 : sortedModes) { + const auto& mode1 = it1->second; + for (const auto it2 : sortedModes) { + const auto& mode2 = it2->second; + + if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) { + return true; + } + } + } + return false; +} + +std::string toString(const RefreshRateSelector::PolicyVariant& policy) { + using namespace std::string_literals; + + return ftl::match( + policy, + [](const RefreshRateSelector::DisplayManagerPolicy& policy) { + return "DisplayManagerPolicy"s + policy.toString(); + }, + [](const RefreshRateSelector::OverridePolicy& policy) { + return "OverridePolicy"s + policy.toString(); + }, + [](RefreshRateSelector::NoOverridePolicy) { return "NoOverridePolicy"s; }); +} + +} // namespace + +auto RefreshRateSelector::createFrameRateModes( + std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const + -> std::vector<FrameRateMode> { + struct Key { + Fps fps; + int32_t group; + }; + + struct KeyLess { + bool operator()(const Key& a, const Key& b) const { + using namespace fps_approx_ops; + if (a.fps != b.fps) { + return a.fps < b.fps; + } + + // For the same fps the order doesn't really matter, but we still + // want the behaviour of a strictly less operator. + // We use the group id as the secondary ordering for that. + return a.group < b.group; + } + }; + + std::map<Key, DisplayModeIterator, KeyLess> ratesMap; + for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) { + const auto& [id, mode] = *it; + + if (!filterModes(*mode)) { + continue; + } + const auto [start, end] = + divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride); + for (auto divisor = start; divisor <= end; divisor++) { + const auto fps = mode->getFps() / divisor; + using fps_approx_ops::operator<; + if (fps < kMinSupportedFrameRate) { + break; + } + + if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled && + !renderRange.includes(fps)) { + continue; + } + + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::AppOverrideNativeRefreshRates && + !isNativeRefreshRate(fps)) { + continue; + } + + const auto [existingIter, emplaceHappened] = + ratesMap.try_emplace(Key{fps, mode->getGroup()}, it); + if (emplaceHappened) { + ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(), + to_string(mode->getFps()).c_str()); + } else { + // We might need to update the map as we found a lower refresh rate + if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) { + existingIter->second = it; + ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(), + to_string(mode->getFps()).c_str()); + } + } + } + } + + std::vector<FrameRateMode> frameRateModes; + frameRateModes.reserve(ratesMap.size()); + for (const auto& [key, mode] : ratesMap) { + frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)}); + } + + // We always want that the lowest frame rate will be corresponding to the + // lowest mode for power saving. + const auto lowestRefreshRateIt = + std::min_element(frameRateModes.begin(), frameRateModes.end(), + [](const FrameRateMode& lhs, const FrameRateMode& rhs) { + return isStrictlyLess(lhs.modePtr->getFps(), + rhs.modePtr->getFps()); + }); + frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt); + + return frameRateModes; +} + +struct RefreshRateSelector::RefreshRateScoreComparator { + bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const { + const auto& [frameRateMode, overallScore, _] = lhs; + + std::string name = to_string(frameRateMode); + + ALOGV("%s sorting scores %.2f", name.c_str(), overallScore); + ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100))); + + if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) { + return overallScore > rhs.overallScore; + } + + if (refreshRateOrder == RefreshRateOrder::Descending) { + using fps_approx_ops::operator>; + return frameRateMode.fps > rhs.frameRateMode.fps; + } else { + using fps_approx_ops::operator<; + return frameRateMode.fps < rhs.frameRateMode.fps; + } + } + + const RefreshRateOrder refreshRateOrder; +}; + +std::string RefreshRateSelector::Policy::toString() const { + return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" + ", primaryRanges=%s, appRequestRanges=%s}", + defaultMode.value(), allowGroupSwitching ? "true" : "false", + to_string(primaryRanges).c_str(), + to_string(appRequestRanges).c_str()); +} + +std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod, + nsecs_t displayPeriod) const { + auto [quotient, remainder] = std::div(layerPeriod, displayPeriod); + if (remainder <= MARGIN_FOR_PERIOD_CALCULATION || + std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { + quotient++; + remainder = 0; + } + + return {quotient, remainder}; +} + +float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer, + Fps refreshRate) const { + constexpr float kScoreForFractionalPairs = .8f; + + const auto displayPeriod = refreshRate.getPeriodNsecs(); + const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs(); + if (layer.vote == LayerVoteType::ExplicitDefault) { + // Find the actual rate the layer will render, assuming + // that layerPeriod is the minimal period to render a frame. + // For example if layerPeriod is 20ms and displayPeriod is 16ms, + // then the actualLayerPeriod will be 32ms, because it is the + // smallest multiple of the display period which is >= layerPeriod. + auto actualLayerPeriod = displayPeriod; + int multiplier = 1; + while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) { + multiplier++; + actualLayerPeriod = displayPeriod * multiplier; + } + + // Because of the threshold we used above it's possible that score is slightly + // above 1. + return std::min(1.0f, + static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod)); + } + + if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || + layer.vote == LayerVoteType::Heuristic) { + const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue(); + + // We only want to score this layer as a fractional pair if the content is not + // significantly faster than the display rate, at it would cause a significant frame drop. + // It is more appropriate to choose a higher display rate even if + // a pull-down will be required. + constexpr float kMinMultiplier = 0.25f; + if (multiplier >= kMinMultiplier && + isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) { + return kScoreForFractionalPairs; + } + + // Calculate how many display vsyncs we need to present a single frame for this + // layer + const auto [displayFramesQuotient, displayFramesRemainder] = + getDisplayFrames(layerPeriod, displayPeriod); + static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 + if (displayFramesRemainder == 0) { + // Layer desired refresh rate matches the display rate. + return 1.0f; + } + + if (displayFramesQuotient == 0) { + // Layer desired refresh rate is higher than the display rate. + return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * + (1.0f / (MAX_FRAMES_TO_FIT + 1)); + } + + // Layer desired refresh rate is lower than the display rate. Check how well it fits + // the cadence. + auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder)); + int iter = 2; + while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { + diff = diff - (displayPeriod - diff); + iter++; + } + + return (1.0f / iter); + } + + return 0; +} + +float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const { + const auto& maxFps = mAppRequestFrameRates.back().fps; + const float ratio = refreshRate.getValue() / maxFps.getValue(); + // Use ratio^2 to get a lower score the more we get further from peak + return ratio * ratio; +} + +float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, + bool isSeamlessSwitch) const { + // Slightly prefer seamless switches. + constexpr float kSeamedSwitchPenalty = 0.95f; + const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; + + // If the layer wants Max, give higher score to the higher refresh rate + if (layer.vote == LayerVoteType::Max) { + return calculateDistanceScoreFromMax(refreshRate); + } + + if (layer.vote == LayerVoteType::ExplicitExact) { + const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate); + if (supportsAppFrameRateOverrideByContent()) { + // Since we support frame rate override, allow refresh rates which are + // multiples of the layer's request, as those apps would be throttled + // down to run at the desired refresh rate. + return divisor > 0; + } + + return divisor == 1; + } + + // If the layer frame rate is a divisor of the refresh rate it should score + // the highest score. + if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) { + return 1.0f * seamlessness; + } + + // The layer frame rate is not a divisor of the refresh rate, + // there is a small penalty attached to the score to favor the frame rates + // the exactly matches the display refresh rate or a multiple. + constexpr float kNonExactMatchingPenalty = 0.95f; + return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness * + kNonExactMatchingPenalty; +} + +auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const -> RankedFrameRates { + std::lock_guard lock(mLock); + + if (mGetRankedFrameRatesCache && + mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) { + return mGetRankedFrameRatesCache->result; + } + + const auto result = getRankedFrameRatesLocked(layers, signals); + mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result}; + return result; +} + +auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const + -> RankedFrameRates { + using namespace fps_approx_ops; + ATRACE_CALL(); + ALOGV("%s: %zu layers", __func__, layers.size()); + + const auto& activeMode = *getActiveModeLocked().modePtr; + + // Keep the display at max frame rate for the duration of powering on the display. + if (signals.powerOnImminent) { + ALOGV("Power On Imminent"); + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending), + GlobalSignals{.powerOnImminent = true}}; + } + + int noVoteLayers = 0; + int minVoteLayers = 0; + int maxVoteLayers = 0; + int explicitDefaultVoteLayers = 0; + int explicitExactOrMultipleVoteLayers = 0; + int explicitExact = 0; + int seamedFocusedLayers = 0; + + for (const auto& layer : layers) { + switch (layer.vote) { + case LayerVoteType::NoVote: + noVoteLayers++; + break; + case LayerVoteType::Min: + minVoteLayers++; + break; + case LayerVoteType::Max: + maxVoteLayers++; + break; + case LayerVoteType::ExplicitDefault: + explicitDefaultVoteLayers++; + break; + case LayerVoteType::ExplicitExactOrMultiple: + explicitExactOrMultipleVoteLayers++; + break; + case LayerVoteType::ExplicitExact: + explicitExact++; + break; + case LayerVoteType::Heuristic: + break; + } + + if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) { + seamedFocusedLayers++; + } + } + + const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 || + explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0; + + const Policy* policy = getCurrentPolicyLocked(); + const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get(); + + // If the default mode group is different from the group of current mode, + // this means a layer requesting a seamed mode switch just disappeared and + // we should switch back to the default group. + // However if a seamed layer is still present we anchor around the group + // of the current mode, in order to prevent unnecessary seamed mode switches + // (e.g. when pausing a video playback). + const auto anchorGroup = + seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup(); + + // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've + // selected a refresh rate to see if we should apply touch boost. + if (signals.touch && !hasExplicitVoteLayers) { + ALOGV("Touch Boost"); + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), + GlobalSignals{.touch = true}}; + } + + // If the primary range consists of a single refresh rate then we can only + // move out the of range if layers explicitly request a different refresh + // rate. + const bool primaryRangeIsSingleRate = + isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max); + + if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { + ALOGV("Idle"); + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), + GlobalSignals{.idle = true}}; + } + + if (layers.empty() || noVoteLayers == layers.size()) { + ALOGV("No layers with votes"); + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; + } + + // Only if all layers want Min we should return Min + if (noVoteLayers + minVoteLayers == layers.size()) { + ALOGV("All layers Min"); + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals}; + } + + // Find the best refresh rate based on score + std::vector<RefreshRateScore> scores; + scores.reserve(mAppRequestFrameRates.size()); + + for (const FrameRateMode& it : mAppRequestFrameRates) { + scores.emplace_back(RefreshRateScore{it, 0.0f}); + } + + for (const auto& layer : layers) { + ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(), + ftl::enum_string(layer.vote).c_str(), layer.weight, + layer.desiredRefreshRate.getValue()); + if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { + continue; + } + + const auto weight = layer.weight; + + for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) { + const auto& [fps, modePtr] = mode; + const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup(); + + if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) { + ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s", + formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(), + to_string(activeMode).c_str()); + continue; + } + + if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch && + !layer.focused) { + ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed." + " Current mode = %s", + formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(), + to_string(activeMode).c_str()); + continue; + } + + // Layers with default seamlessness vote for the current mode group if + // there are layers with seamlessness=SeamedAndSeamless and for the default + // mode group otherwise. In second case, if the current mode group is different + // from the default, this means a layer with seamlessness=SeamedAndSeamless has just + // disappeared. + const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup; + if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { + ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), + to_string(*modePtr).c_str(), to_string(activeMode).c_str()); + continue; + } + + const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps()); + if ((primaryRangeIsSingleRate || !inPrimaryRange) && + !(layer.focused && + (layer.vote == LayerVoteType::ExplicitDefault || + layer.vote == LayerVoteType::ExplicitExact))) { + // Only focused layers with ExplicitDefault frame rate settings are allowed to score + // refresh rates outside the primary range. + continue; + } + + const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch); + const float weightedLayerScore = weight * layerScore; + + // Layer with fixed source has a special consideration which depends on the + // mConfig.frameRateMultipleThreshold. We don't want these layers to score + // refresh rates above the threshold, but we also don't want to favor the lower + // ones by having a greater number of layers scoring them. Instead, we calculate + // the score independently for these layers and later decide which + // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not + // score 120 Hz, but desired 60 fps should contribute to the score. + const bool fixedSourceLayer = [](LayerVoteType vote) { + switch (vote) { + case LayerVoteType::ExplicitExactOrMultiple: + case LayerVoteType::Heuristic: + return true; + case LayerVoteType::NoVote: + case LayerVoteType::Min: + case LayerVoteType::Max: + case LayerVoteType::ExplicitDefault: + case LayerVoteType::ExplicitExact: + return false; + } + }(layer.vote); + const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 && + layer.desiredRefreshRate < + Fps::fromValue(mConfig.frameRateMultipleThreshold / 2); + if (fixedSourceLayer && layerBelowThreshold) { + const bool modeAboveThreshold = + modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); + if (modeAboveThreshold) { + ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(), + to_string(modePtr->getFps()).c_str(), layerScore); + fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore; + } else { + ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(), + to_string(modePtr->getFps()).c_str(), layerScore); + fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore; + } + } else { + ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(), + to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore); + overallScore += weightedLayerScore; + } + } + } + + // We want to find the best refresh rate without the fixed source layers, + // so we could know whether we should add the modeAboveThreshold scores or not. + // If the best refresh rate is already above the threshold, it means that + // some non-fixed source layers already scored it, so we can just add the score + // for all fixed source layers, even the ones that are above the threshold. + const bool maxScoreAboveThreshold = [&] { + if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) { + return false; + } + + const auto maxScoreIt = + std::max_element(scores.begin(), scores.end(), + [](RefreshRateScore max, RefreshRateScore current) { + return current.overallScore > max.overallScore; + }); + ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the " + "threshold for " + "refresh rate multiples", + to_string(maxScoreIt->frameRateMode.fps).c_str(), + to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(), + maxScoreAboveThreshold ? "above" : "below"); + return maxScoreIt->frameRateMode.modePtr->getFps() >= + Fps::fromValue(mConfig.frameRateMultipleThreshold); + }(); + + // Now we can add the fixed rate layers score + for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) { + overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold; + if (maxScoreAboveThreshold) { + overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold; + } + ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(), + to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore); + } + + // Now that we scored all the refresh rates we need to pick the one that got the highest + // overallScore. Sort the scores based on their overallScore in descending order of priority. + const RefreshRateOrder refreshRateOrder = + maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending; + std::sort(scores.begin(), scores.end(), + RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder}); + + FrameRateRanking ranking; + ranking.reserve(scores.size()); + + std::transform(scores.begin(), scores.end(), back_inserter(ranking), + [](const RefreshRateScore& score) { + return ScoredFrameRate{score.frameRateMode, score.overallScore}; + }); + + const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) { + return score.overallScore == 0; + }); + + if (primaryRangeIsSingleRate) { + // If we never scored any layers, then choose the rate from the primary + // range instead of picking a random score from the app range. + if (noLayerScore) { + ALOGV("Layers not scored"); + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; + } else { + return {ranking, kNoSignals}; + } + } + + // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly + // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit + // vote we should not change it if we get a touch event. Only apply touch boost if it will + // actually increase the refresh rate over the normal selection. + const bool touchBoostForExplicitExact = [&] { + if (supportsAppFrameRateOverrideByContent()) { + // Enable touch boost if there are other layers besides exact + return explicitExact + noVoteLayers != layers.size(); + } else { + // Enable touch boost if there are no exact layers + return explicitExact == 0; + } + }(); + + const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); + using fps_approx_ops::operator<; + + if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && + scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { + ALOGV("Touch Boost"); + return {touchRefreshRates, GlobalSignals{.touch = true}}; + } + + // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the + // current config + if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) { + const auto preferredDisplayMode = activeMode.getId(); + return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode), + kNoSignals}; + } + + return {ranking, kNoSignals}; +} + +using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; +using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; + +PerUidLayerRequirements groupLayersByUid( + const std::vector<RefreshRateSelector::LayerRequirement>& layers) { + PerUidLayerRequirements layersByUid; + for (const auto& layer : layers) { + const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; + auto& layersWithSameUid = it->second; + layersWithSameUid.push_back(&layer); + } + + // Remove uids that can't have a frame rate override + for (auto it = layersByUid.begin(); it != layersByUid.end();) { + const auto& layersWithSameUid = it->second; + bool skipUid = false; + for (const auto& layer : layersWithSameUid) { + using LayerVoteType = RefreshRateSelector::LayerVoteType; + + if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) { + skipUid = true; + break; + } + } + if (skipUid) { + it = layersByUid.erase(it); + } else { + ++it; + } + } + + return layersByUid; +} + +auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers, + Fps displayRefreshRate, + GlobalSignals globalSignals) const + -> UidToFrameRateOverride { + ATRACE_CALL(); + if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) { + return {}; + } + + ALOGV("%s: %zu layers", __func__, layers.size()); + std::lock_guard lock(mLock); + + const auto* policyPtr = getCurrentPolicyLocked(); + // We don't want to run lower than 30fps + const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess); + + using fps_approx_ops::operator/; + const unsigned numMultiples = displayRefreshRate / minFrameRate; + + std::vector<std::pair<Fps, float>> scoredFrameRates; + scoredFrameRates.reserve(numMultiples); + + for (unsigned n = numMultiples; n > 0; n--) { + const Fps divisor = displayRefreshRate / n; + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::AppOverrideNativeRefreshRates && + !isNativeRefreshRate(divisor)) { + continue; + } + + if (policyPtr->appRequestRanges.render.includes(divisor)) { + ALOGV("%s: adding %s as a potential frame rate", __func__, to_string(divisor).c_str()); + scoredFrameRates.emplace_back(divisor, 0); + } + } + + const auto layersByUid = groupLayersByUid(layers); + UidToFrameRateOverride frameRateOverrides; + for (const auto& [uid, layersWithSameUid] : layersByUid) { + // Layers with ExplicitExactOrMultiple expect touch boost + const bool hasExplicitExactOrMultiple = + std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(), + [](const auto& layer) { + return layer->vote == LayerVoteType::ExplicitExactOrMultiple; + }); + + if (globalSignals.touch && hasExplicitExactOrMultiple) { + continue; + } + + for (auto& [_, score] : scoredFrameRates) { + score = 0; + } + + for (const auto& layer : layersWithSameUid) { + if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) { + continue; + } + + LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && + layer->vote != LayerVoteType::ExplicitExactOrMultiple && + layer->vote != LayerVoteType::ExplicitExact); + for (auto& [fps, score] : scoredFrameRates) { + constexpr bool isSeamlessSwitch = true; + const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch); + score += layer->weight * layerScore; + } + } + + // If we never scored any layers, we don't have a preferred frame rate + if (std::all_of(scoredFrameRates.begin(), scoredFrameRates.end(), + [](const auto& scoredFrameRate) { + const auto [_, score] = scoredFrameRate; + return score == 0; + })) { + continue; + } + + // Now that we scored all the refresh rates we need to pick the lowest refresh rate + // that got the highest score. + const auto [overrideFps, _] = + *std::max_element(scoredFrameRates.begin(), scoredFrameRates.end(), + [](const auto& lhsPair, const auto& rhsPair) { + const float lhs = lhsPair.second; + const float rhs = rhsPair.second; + return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs); + }); + ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid); + frameRateOverrides.emplace(uid, overrideFps); + } + + return frameRateOverrides; +} + +ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged( + std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const { + std::lock_guard lock(mLock); + + const auto current = [&]() REQUIRES(mLock) -> FrameRateMode { + if (desiredActiveModeId) { + const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get(); + return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)}; + } + + return getActiveModeLocked(); + }(); + + const DisplayModePtr& min = mMinRefreshRateModeIt->second; + if (current.modePtr->getId() == min->getId()) { + return {}; + } + + return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current; +} + +const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const { + const auto& activeMode = *getActiveModeLocked().modePtr; + + for (const FrameRateMode& mode : mPrimaryFrameRates) { + if (activeMode.getGroup() == mode.modePtr->getGroup()) { + return mode.modePtr.get(); + } + } + + ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s", + to_string(activeMode).c_str()); + + // Default to the lowest refresh rate. + return mPrimaryFrameRates.front().modePtr.get(); +} + +const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { + const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr; + const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr; + + bool maxByAnchorFound = false; + for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) { + using namespace fps_approx_ops; + if (it->modePtr->getFps() > (*max)->getFps()) { + max = &it->modePtr; + } + + if (anchorGroup == it->modePtr->getGroup() && + it->modePtr->getFps() >= (*maxByAnchor)->getFps()) { + maxByAnchorFound = true; + maxByAnchor = &it->modePtr; + } + } + + if (maxByAnchorFound) { + return maxByAnchor->get(); + } + + ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup); + + // Default to the highest refresh rate. + return max->get(); +} + +auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt, + RefreshRateOrder refreshRateOrder, + std::optional<DisplayModeId> preferredDisplayModeOpt) const + -> FrameRateRanking { + const char* const whence = __func__; + std::deque<ScoredFrameRate> ranking; + const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) { + const auto& modePtr = frameRateMode.modePtr; + if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) { + return; + } + + float score = calculateDistanceScoreFromMax(frameRateMode.fps); + const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending); + if (inverseScore) { + score = 1.0f / score; + } + if (preferredDisplayModeOpt) { + if (*preferredDisplayModeOpt == modePtr->getId()) { + constexpr float kScore = std::numeric_limits<float>::max(); + ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore}); + return; + } + constexpr float kNonPreferredModePenalty = 0.95f; + score *= kNonPreferredModePenalty; + } + ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(), + to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score); + ranking.emplace_back(ScoredFrameRate{frameRateMode, score}); + }; + + if (refreshRateOrder == RefreshRateOrder::Ascending) { + std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate); + } else { + std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate); + } + + if (!ranking.empty() || !anchorGroupOpt) { + return {ranking.begin(), ranking.end()}; + } + + ALOGW("Can't find %s refresh rate by policy with the same mode group" + " as the mode group %d", + refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value()); + + constexpr std::optional<int> kNoAnchorGroup = std::nullopt; + return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt); +} + +FrameRateMode RefreshRateSelector::getActiveMode() const { + std::lock_guard lock(mLock); + return getActiveModeLocked(); +} + +const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const { + return *mActiveModeOpt; +} + +void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) { + std::lock_guard lock(mLock); + + // Invalidate the cached invocation to getRankedFrameRates. This forces + // the refresh rate to be recomputed on the next call to getRankedFrameRates. + mGetRankedFrameRatesCache.reset(); + + const auto activeModeOpt = mDisplayModes.get(modeId); + LOG_ALWAYS_FATAL_IF(!activeModeOpt); + + mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())}); +} + +RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId, + Config config) + : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { + initializeIdleTimer(); + FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId)); +} + +void RefreshRateSelector::initializeIdleTimer() { + if (mConfig.idleTimerTimeout > 0ms) { + mIdleTimer.emplace( + "IdleTimer", mConfig.idleTimerTimeout, + [this] { + std::scoped_lock lock(mIdleTimerCallbacksMutex); + if (const auto callbacks = getIdleTimerCallbacks()) { + callbacks->onReset(); + } + }, + [this] { + std::scoped_lock lock(mIdleTimerCallbacksMutex); + if (const auto callbacks = getIdleTimerCallbacks()) { + callbacks->onExpired(); + } + }); + } +} + +void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { + std::lock_guard lock(mLock); + + // Invalidate the cached invocation to getRankedFrameRates. This forces + // the refresh rate to be recomputed on the next call to getRankedFrameRates. + mGetRankedFrameRatesCache.reset(); + + mDisplayModes = std::move(modes); + const auto activeModeOpt = mDisplayModes.get(activeModeId); + LOG_ALWAYS_FATAL_IF(!activeModeOpt); + mActiveModeOpt = + FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())}; + + const auto sortedModes = sortByRefreshRate(mDisplayModes); + mMinRefreshRateModeIt = sortedModes.front(); + mMaxRefreshRateModeIt = sortedModes.back(); + + // Reset the policy because the old one may no longer be valid. + mDisplayManagerPolicy = {}; + mDisplayManagerPolicy.defaultMode = activeModeId; + + mFrameRateOverrideConfig = [&] { + switch (mConfig.enableFrameRateOverride) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverride: + case Config::FrameRateOverride::Enabled: + return mConfig.enableFrameRateOverride; + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + return shouldEnableFrameRateOverride(sortedModes) + ? Config::FrameRateOverride::AppOverrideNativeRefreshRates + : Config::FrameRateOverride::Disabled; + } + }(); + + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::AppOverrideNativeRefreshRates) { + for (const auto& [_, mode] : mDisplayModes) { + mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit); + } + } + + constructAvailableRefreshRates(); +} + +bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const { + // defaultMode must be a valid mode, and within the given refresh rate range. + if (const auto mode = mDisplayModes.get(policy.defaultMode)) { + if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) { + ALOGE("Default mode is not in the primary range."); + return false; + } + } else { + ALOGE("Default mode is not found."); + return false; + } + + const auto& primaryRanges = policy.primaryRanges; + const auto& appRequestRanges = policy.appRequestRanges; + ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical), + "Physical range is invalid: primary: %s appRequest: %s", + to_string(primaryRanges.physical).c_str(), + to_string(appRequestRanges.physical).c_str()); + ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), + "Render range is invalid: primary: %s appRequest: %s", + to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str()); + + return primaryRanges.valid() && appRequestRanges.valid(); +} + +auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult { + Policy oldPolicy; + PhysicalDisplayId displayId; + { + std::lock_guard lock(mLock); + oldPolicy = *getCurrentPolicyLocked(); + + const bool valid = ftl::match( + policy, + [this](const auto& policy) { + ftl::FakeGuard guard(mLock); + if (!isPolicyValidLocked(policy)) { + ALOGE("Invalid policy: %s", policy.toString().c_str()); + return false; + } + + using T = std::decay_t<decltype(policy)>; + + if constexpr (std::is_same_v<T, DisplayManagerPolicy>) { + mDisplayManagerPolicy = policy; + } else { + static_assert(std::is_same_v<T, OverridePolicy>); + mOverridePolicy = policy; + } + return true; + }, + [this](NoOverridePolicy) { + ftl::FakeGuard guard(mLock); + mOverridePolicy.reset(); + return true; + }); + + if (!valid) { + return SetPolicyResult::Invalid; + } + + mGetRankedFrameRatesCache.reset(); + + if (*getCurrentPolicyLocked() == oldPolicy) { + return SetPolicyResult::Unchanged; + } + constructAvailableRefreshRates(); + + displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId(); + } + + const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u); + + ALOGI("Display %s policy changed\n" + "Previous: %s\n" + "Current: %s\n" + "%u mode changes were performed under the previous policy", + to_string(displayId).c_str(), oldPolicy.toString().c_str(), toString(policy).c_str(), + numModeChanges); + + return SetPolicyResult::Changed; +} + +auto RefreshRateSelector::getCurrentPolicyLocked() const -> const Policy* { + return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy; +} + +auto RefreshRateSelector::getCurrentPolicy() const -> Policy { + std::lock_guard lock(mLock); + return *getCurrentPolicyLocked(); +} + +auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy { + std::lock_guard lock(mLock); + return mDisplayManagerPolicy; +} + +bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const { + std::lock_guard lock(mLock); + return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) != + mAppRequestFrameRates.end(); +} + +void RefreshRateSelector::constructAvailableRefreshRates() { + // Filter modes based on current policy and sort on refresh rate. + const Policy* policy = getCurrentPolicyLocked(); + ALOGV("%s: %s ", __func__, policy->toString().c_str()); + + const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get(); + + const auto filterRefreshRates = [&](const FpsRanges& ranges, + const char* rangeName) REQUIRES(mLock) { + const auto filterModes = [&](const DisplayMode& mode) { + return mode.getResolution() == defaultMode->getResolution() && + mode.getDpi() == defaultMode->getDpi() && + (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) && + ranges.physical.includes(mode.getFps()) && + (supportsFrameRateOverride() || ranges.render.includes(mode.getFps())); + }; + + const auto frameRateModes = createFrameRateModes(filterModes, ranges.render); + LOG_ALWAYS_FATAL_IF(frameRateModes.empty(), + "No matching frame rate modes for %s range. policy: %s", rangeName, + policy->toString().c_str()); + + const auto stringifyModes = [&] { + std::string str; + for (const auto& frameRateMode : frameRateModes) { + str += to_string(frameRateMode) + " "; + } + return str; + }; + ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str()); + + return frameRateModes; + }; + + mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary"); + mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request"); +} + +Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const { + using namespace fps_approx_ops; + + if (frameRate <= mKnownFrameRates.front()) { + return mKnownFrameRates.front(); + } + + if (frameRate >= mKnownFrameRates.back()) { + return mKnownFrameRates.back(); + } + + auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate, + isStrictlyLess); + + const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue()); + const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue()); + return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); +} + +auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction { + std::lock_guard lock(mLock); + + const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps(); + const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked(); + + // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that + // the min allowed refresh rate is higher than the device min, we do not want to enable the + // timer. + if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) { + return KernelIdleTimerAction::TurnOff; + } + + const DisplayModePtr& maxByPolicy = + getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()); + if (minByPolicy == maxByPolicy) { + // Turn on the timer when the min of the primary range is below the device min. + if (const Policy* currentPolicy = getCurrentPolicyLocked(); + isApproxLess(currentPolicy->primaryRanges.physical.min, deviceMinFps)) { + return KernelIdleTimerAction::TurnOn; + } + return KernelIdleTimerAction::TurnOff; + } + + // Turn on the timer in all other cases. + return KernelIdleTimerAction::TurnOn; +} + +int RefreshRateSelector::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) { + // This calculation needs to be in sync with the java code + // in DisplayManagerService.getDisplayInfoForFrameRateOverride + + // The threshold must be smaller than 0.001 in order to differentiate + // between the fractional pairs (e.g. 59.94 and 60). + constexpr float kThreshold = 0.0009f; + const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue(); + const auto numPeriodsRounded = std::round(numPeriods); + if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { + return 0; + } + + return static_cast<int>(numPeriodsRounded); +} + +bool RefreshRateSelector::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { + if (isStrictlyLess(bigger, smaller)) { + return isFractionalPairOrMultiple(bigger, smaller); + } + + const auto multiplier = std::round(bigger.getValue() / smaller.getValue()); + constexpr float kCoef = 1000.f / 1001.f; + return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) || + isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef)); +} + +void RefreshRateSelector::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; + + std::lock_guard lock(mLock); + + const auto activeMode = getActiveModeLocked(); + dumper.dump("activeMode"sv, to_string(activeMode)); + + dumper.dump("displayModes"sv); + { + utils::Dumper::Indent indent(dumper); + for (const auto& [id, mode] : mDisplayModes) { + dumper.dump({}, to_string(*mode)); + } + } + + dumper.dump("displayManagerPolicy"sv, mDisplayManagerPolicy.toString()); + + if (const Policy& currentPolicy = *getCurrentPolicyLocked(); + mOverridePolicy && currentPolicy != mDisplayManagerPolicy) { + dumper.dump("overridePolicy"sv, currentPolicy.toString()); + } + + dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig)); + + dumper.dump("idleTimer"sv); + { + utils::Dumper::Indent indent(dumper); + dumper.dump("interval"sv, mIdleTimer.transform(&OneShotTimer::interval)); + dumper.dump("controller"sv, + mConfig.kernelIdleTimerController + .and_then(&ftl::enum_name<KernelIdleTimerController>) + .value_or("Platform"sv)); + } +} + +std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { + return mConfig.idleTimerTimeout; +} + +} // namespace android::scheduler + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index a79002e959..4f5842a67a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -18,19 +18,25 @@ #include <algorithm> #include <numeric> -#include <optional> #include <type_traits> #include <utility> +#include <variant> +#include <ftl/concat.h> +#include <ftl/optional.h> +#include <ftl/unit.h> #include <gui/DisplayEventReceiver.h> #include <scheduler/Fps.h> +#include <scheduler/FrameRateMode.h> #include <scheduler/Seamlessness.h> #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/HWComposer.h" #include "Scheduler/OneShotTimer.h" #include "Scheduler/StrongTyping.h" +#include "ThreadContext.h" +#include "Utils/Dumper.h" namespace android::scheduler { @@ -45,19 +51,19 @@ inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; -/** - * 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. - */ -class RefreshRateConfigs { +// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with +// the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer, +// and `GlobalSignals`. +class RefreshRateSelector { public: // Margin used when matching refresh rates to the content desired ones. static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = std::chrono::nanoseconds(800us).count(); - struct Policy { - private: + // The lowest Render Frame Rate that will ever be selected + static constexpr Fps kMinSupportedFrameRate = 20_Hz; + + class Policy { static constexpr int kAllowGroupSwitchingDefault = false; public: @@ -66,40 +72,31 @@ public: DisplayModeId defaultMode; // Whether or not we switch mode groups to get the best frame rate. bool allowGroupSwitching = kAllowGroupSwitchingDefault; - // The primary refresh rate range represents display manager's general guidance on the - // display modes we'll consider when switching refresh rates. Unless we get an explicit - // signal from an app, we should stay within this range. - FpsRange primaryRange; - // The app request refresh rate range allows us to consider more display modes when - // switching refresh rates. Although we should generally stay within the primary range, - // specific considerations, such as layer frame rate settings specified via the - // setFrameRate() api, may cause us to go outside the primary range. We never go outside the - // app request range. The app request range will be greater than or equal to the primary - // refresh rate range, never smaller. - FpsRange appRequestRange; + // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details. + // TODO(b/257072060): use the render range when selecting SF render rate + // or the app override frame rate + FpsRanges primaryRanges; + // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details. + FpsRanges appRequestRanges; Policy() = default; - Policy(DisplayModeId defaultMode, FpsRange range) - : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {} - - Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range) - : Policy(defaultMode, allowGroupSwitching, range, range) {} - - Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange) - : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {} + Policy(DisplayModeId defaultMode, FpsRange range, + bool allowGroupSwitching = kAllowGroupSwitchingDefault) + : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range}, + allowGroupSwitching) {} - Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange, - FpsRange appRequestRange) + Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges, + bool allowGroupSwitching = kAllowGroupSwitchingDefault) : defaultMode(defaultMode), allowGroupSwitching(allowGroupSwitching), - primaryRange(primaryRange), - appRequestRange(appRequestRange) {} + primaryRanges(primaryRanges), + appRequestRanges(appRequestRanges) {} bool operator==(const Policy& other) const { using namespace fps_approx_ops; - return defaultMode == other.defaultMode && primaryRange == other.primaryRange && - appRequestRange == other.appRequestRange && + return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges && + appRequestRanges == other.appRequestRanges && allowGroupSwitching == other.allowGroupSwitching; } @@ -107,23 +104,28 @@ public: std::string toString() const; }; - // Return code set*Policy() to indicate the current policy is unchanged. - static constexpr int CURRENT_POLICY_UNCHANGED = 1; + enum class SetPolicyResult { Invalid, Unchanged, Changed }; // We maintain the display manager policy and the override policy separately. The override // policy is used by CTS tests to get a consistent device state for testing. While the override // policy is set, it takes precedence over the display manager policy. Once the override policy // is cleared, we revert to using the display manager policy. + struct DisplayManagerPolicy : Policy { + using Policy::Policy; + }; + + struct OverridePolicy : Policy { + using Policy::Policy; + }; + + struct NoOverridePolicy {}; + + using PolicyVariant = std::variant<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>; + + SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext); + + void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; } - // Sets the display manager policy to choose refresh rates. The return value will be: - // - A negative value if the policy is invalid or another error occurred. - // - NO_ERROR if the policy was successfully updated, and the current policy is different from - // what it was before the call. - // - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy - // is the same as it was before the call. - status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock); - // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value. - status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock); // Gets the current policy, which will be the override policy if active, and the display manager // policy otherwise. Policy getCurrentPolicy() const EXCLUDES(mLock); @@ -131,7 +133,7 @@ public: Policy getDisplayManagerPolicy() const EXCLUDES(mLock); // Returns true if mode is allowed by the current policy. - bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock); + bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock); // Describes the different options the layer voted for refresh rate enum class LayerVoteType { @@ -184,31 +186,68 @@ public: bool touch = false; // True if the system hasn't seen any buffers posted to layers recently. bool idle = false; + // Whether the display is about to be powered on, or has been in PowerMode::ON + // within the timeout of DisplayPowerTimer. + bool powerOnImminent = false; bool operator==(GlobalSignals other) const { - return touch == other.touch && idle == other.idle; + return touch == other.touch && idle == other.idle && + powerOnImminent == other.powerOnImminent; + } + + auto toString() const { + return ftl::Concat("{touch=", touch, ", idle=", idle, + ", powerOnImminent=", powerOnImminent, '}'); + } + }; + + struct ScoredFrameRate { + FrameRateMode frameRateMode; + float score = 0.0f; + + bool operator==(const ScoredFrameRate& other) const { + return frameRateMode == other.frameRateMode && score == other.score; + } + + static bool scoresEqual(float lhs, float rhs) { + constexpr float kEpsilon = 0.0001f; + return std::abs(lhs - rhs) <= kEpsilon; + } + + struct DescendingScore { + bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const { + return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score); + } + }; + }; + + using FrameRateRanking = std::vector<ScoredFrameRate>; + + struct RankedFrameRates { + FrameRateRanking ranking; // Ordered by descending score. + GlobalSignals consideredSignals; + + bool operator==(const RankedFrameRates& other) const { + return ranking == other.ranking && consideredSignals == other.consideredSignals; } }; - // Returns the refresh rate that best fits the given layers, and whether the refresh rate was - // chosen based on touch boost and/or idle timer. - std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate( - const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock); + RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const + EXCLUDES(mLock); FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { std::lock_guard lock(mLock); return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()}; } - std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId, - bool timerExpired) const EXCLUDES(mLock); + ftl::Optional<FrameRateMode> onKernelTimerChanged( + std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const + EXCLUDES(mLock); - // Returns the highest refresh rate according to the current policy. May change at runtime. Only - // uses the primary range, not the app request range. - DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock); + void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock); - void setActiveModeId(DisplayModeId) EXCLUDES(mLock); - DisplayModePtr getActiveMode() const EXCLUDES(mLock); + // See mActiveModeOpt for thread safety. + FrameRateMode getActiveMode() const EXCLUDES(mLock); // Returns a known frame rate that is the closest to frameRate Fps findClosestKnownFrameRate(Fps frameRate) const; @@ -217,7 +256,23 @@ public: // Configuration flags. struct Config { - bool enableFrameRateOverride = false; + enum class FrameRateOverride { + // Do not override the frame rate for an app + Disabled, + + // Override the frame rate for an app to a value which is also + // a display refresh rate + AppOverrideNativeRefreshRates, + + // Override the frame rate for an app to any value + AppOverride, + + // Override the frame rate for all apps and all values. + Enabled, + + ftl_last = Enabled + }; + FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled; // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if @@ -229,37 +284,47 @@ public: // The controller representing how the kernel idle timer will be configured // either on the HWC api or sysprop. - std::optional<KernelIdleTimerController> kernelIdleTimerController; + ftl::Optional<KernelIdleTimerController> kernelIdleTimerController; }; - RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId, - Config config = {.enableFrameRateOverride = false, - .frameRateMultipleThreshold = 0, - .idleTimerTimeout = 0ms, - .kernelIdleTimerController = {}}); + RefreshRateSelector( + DisplayModes, DisplayModeId activeModeId, + Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled, + .frameRateMultipleThreshold = 0, + .idleTimerTimeout = 0ms, + .kernelIdleTimerController = {}}); - RefreshRateConfigs(const RefreshRateConfigs&) = delete; - RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete; + RefreshRateSelector(const RefreshRateSelector&) = delete; + RefreshRateSelector& operator=(const RefreshRateSelector&) = delete; // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only - // differ in resolution. + // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default, + // we can probably remove canSwitch altogether since all devices will be able + // to switch to a frame rate divisor. bool canSwitch() const EXCLUDES(mLock) { std::lock_guard lock(mLock); - return mDisplayModes.size() > 1; + return mDisplayModes.size() > 1 || + mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; } // Class to enumerate options around toggling the kernel timer on and off. enum class KernelIdleTimerAction { - TurnOff, // Turn off the idle timer. - TurnOn // Turn on the idle timer. + TurnOff, // Turn off the idle timer. + TurnOn // Turn on the idle timer. }; // Checks whether kernel idle timer should be active depending the policy decisions around // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; - bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; } + bool supportsAppFrameRateOverrideByContent() const { + return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled; + } + + bool supportsFrameRateOverride() const { + return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; + } // Return the display refresh rate divisor to match the layer // frame rate, or 0 if the display refresh rate is not a multiple of the @@ -313,27 +378,32 @@ public: } } - void resetIdleTimer(bool kernelOnly) { - if (!mIdleTimer) { - return; + void resetKernelIdleTimer() { + if (mIdleTimer && mConfig.kernelIdleTimerController) { + mIdleTimer->reset(); } - if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) { - return; + } + + void resetIdleTimer() { + if (mIdleTimer) { + mIdleTimer->reset(); } - mIdleTimer->reset(); } - void dump(std::string& result) const EXCLUDES(mLock); + void dump(utils::Dumper&) const EXCLUDES(mLock); std::chrono::milliseconds getIdleTimerTimeout(); private: - friend struct TestableRefreshRateConfigs; + friend struct TestableRefreshRateSelector; void constructAvailableRefreshRates() REQUIRES(mLock); - std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked( - const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock); + // See mActiveModeOpt for thread safety. + const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock); + + RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const REQUIRES(mLock); // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. @@ -346,13 +416,27 @@ private: // Returns the highest refresh rate according to the current policy. May change at runtime. Only // uses the primary range, not the app request range. const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock); - const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) { - return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup()); - } + + struct RefreshRateScoreComparator; + + enum class RefreshRateOrder { + Ascending, + Descending, + + ftl_last = Descending + }; + + // Only uses the primary range, not the app request range. + FrameRateRanking rankFrameRates( + std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder, + std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const + REQUIRES(mLock); const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); + // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1. + float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock); // calculates a score for a layer. Used to determine the display refresh rate // and the frame rate override for certains applications. float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate, @@ -361,7 +445,8 @@ private: float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const REQUIRES(mLock); - void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock); + void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock) + REQUIRES(kMainThreadContext); void initializeIdleTimer(); @@ -372,22 +457,41 @@ private: : mIdleTimerCallbacks->platform; } + bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) { + LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride != + Config::FrameRateOverride::AppOverrideNativeRefreshRates, + "should only be called when " + "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used"); + return mAppOverrideNativeRefreshRates.contains(fps); + } + + std::vector<FrameRateMode> createFrameRateModes( + std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const + REQUIRES(mLock); + // The display modes of the active display. The DisplayModeIterators below are pointers into // this container, so must be invalidated whenever the DisplayModes change. The Policy below // is also dependent, so must be reset as well. DisplayModes mDisplayModes GUARDED_BY(mLock); - DisplayModeIterator mActiveModeIt GUARDED_BY(mLock); + // Set of supported display refresh rates for easy lookup + // when FrameRateOverride::AppOverrideNativeRefreshRates is in use. + ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates; + + ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock); + DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock); DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock); // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate. - std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock); - std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock); + std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock); + std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock); Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); + unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0; + mutable std::mutex mLock; // A sorted list of known frame rates that a Heuristic layer will choose @@ -395,19 +499,19 @@ private: const std::vector<Fps> mKnownFrameRates; const Config mConfig; - bool mSupportsFrameRateOverrideByContent; + Config::FrameRateOverride mFrameRateOverrideConfig; - struct GetBestRefreshRateCache { + struct GetRankedFrameRatesCache { std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments; - std::pair<DisplayModePtr, GlobalSignals> result; + RankedFrameRates result; }; - mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock); + mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock); // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed. std::mutex mIdleTimerCallbacksMutex; std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex); // Used to detect (lack of) frame activity. - std::optional<scheduler::OneShotTimer> mIdleTimer; + ftl::Optional<scheduler::OneShotTimer> mIdleTimer; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 727cb0817e..34f8df28e8 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,10 +25,11 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> +#include <ftl/enum.h> #include <ftl/fake_guard.h> +#include <ftl/small_map.h> #include <gui/WindowInfo.h> #include <system/window.h> -#include <ui/DisplayStatInfo.h> #include <utils/Timers.h> #include <utils/Trace.h> @@ -42,9 +43,9 @@ #include "../Layer.h" #include "DispSyncSource.h" +#include "Display/DisplayMap.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" -#include "InjectVSyncSource.h" #include "OneShotTimer.h" #include "SurfaceFlingerProperties.h" #include "VSyncPredictor.h" @@ -68,8 +69,8 @@ Scheduler::~Scheduler() { mDisplayPowerTimer.reset(); mTouchTimer.reset(); - // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler. - setRefreshRateConfigs(nullptr); + // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler. + demoteLeaderDisplay(); } void Scheduler::startTimers() { @@ -94,36 +95,29 @@ void Scheduler::startTimers() { } } -void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) { - // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. - { - // mRefreshRateConfigsLock is not locked here to avoid the deadlock - // as the callback can attempt to acquire the lock before stopIdleTimer can finish - // the execution. It's safe to FakeGuard as main thread is the only thread that - // writes to the mRefreshRateConfigs. - ftl::FakeGuard guard(mRefreshRateConfigsLock); - if (mRefreshRateConfigs) { - mRefreshRateConfigs->stopIdleTimer(); - mRefreshRateConfigs->clearIdleTimerCallbacks(); - } - } - { - // Clear state that depends on the current instance. - std::scoped_lock lock(mPolicyLock); - mPolicy = {}; - } +void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) { + demoteLeaderDisplay(); + + std::scoped_lock lock(mDisplayLock); + promoteLeaderDisplay(leaderIdOpt); +} - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs = std::move(configs); - if (!mRefreshRateConfigs) return; +void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { + demoteLeaderDisplay(); - mRefreshRateConfigs->setIdleTimerCallbacks( - {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, - .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); + std::scoped_lock lock(mDisplayLock); + mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr)); - mRefreshRateConfigs->startIdleTimer(); + promoteLeaderDisplay(); +} + +void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { + demoteLeaderDisplay(); + + std::scoped_lock lock(mDisplayLock); + mRefreshRateSelectors.erase(displayId); + + promoteLeaderDisplay(); } void Scheduler::run() { @@ -132,6 +126,18 @@ void Scheduler::run() { } } +void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, + TimePoint expectedVsyncTime) { + const TimePoint frameTime = SchedulerClock::now(); + + if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) { + return; + } + + compositor.composite(frameTime, vsyncId); + compositor.sample(); +} + void Scheduler::createVsyncSchedule(FeatureFlags features) { mVsyncSchedule.emplace(features); } @@ -145,42 +151,38 @@ std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( } std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { - const auto refreshRateConfigs = holdRefreshRateConfigs(); const bool supportsFrameRateOverrideByContent = - refreshRateConfigs->supportsFrameRateOverrideByContent(); + leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); return mFrameRateOverrideMappings .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent); } -bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { +bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const { const auto frameRate = getFrameRateOverride(uid); if (!frameRate.has_value()) { return true; } - return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate); + return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate); } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { - std::scoped_lock lock(mRefreshRateConfigsLock); - return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { - return !isVsyncValid(expectedVsyncTimestamp, uid); + return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid); }; } impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const { return [this](uid_t uid) { - const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps(); - const auto currentPeriod = - mVsyncSchedule->getTracker().currentPeriod() ?: refreshRate.getPeriodNsecs(); + const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps; + const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs(); const auto frameRate = getFrameRateOverride(uid); if (!frameRate.has_value()) { return currentPeriod; } - const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate); + const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate); if (divisor <= 1) { return currentPeriod; } @@ -188,15 +190,14 @@ impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction( }; } -ConnectionHandle Scheduler::createConnection( - const char* connectionName, frametimeline::TokenManager* tokenManager, - std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, - impl::EventThread::InterceptVSyncsCallback interceptCallback) { +ConnectionHandle Scheduler::createConnection(const char* connectionName, + frametimeline::TokenManager* tokenManager, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration); auto throttleVsync = makeThrottleVsyncCallback(); auto getVsyncPeriod = makeGetVsyncPeriodFunction(); auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager, - std::move(interceptCallback), std::move(throttleVsync), std::move(getVsyncPeriod)); return createConnection(std::move(eventThread)); @@ -214,12 +215,12 @@ ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventT } sp<EventThreadConnection> Scheduler::createConnectionInternal( - EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) { + EventThread* eventThread, EventRegistrationFlags eventRegistration) { return eventThread->createEventConnection([&] { resync(); }, eventRegistration); } sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( - ConnectionHandle handle, ISurfaceComposer::EventRegistrationFlags eventRegistration) { + ConnectionHandle handle, EventRegistrationFlags eventRegistration) { std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle, nullptr); return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration); @@ -266,9 +267,8 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { } void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { - const auto refreshRateConfigs = holdRefreshRateConfigs(); const bool supportsFrameRateOverrideByContent = - refreshRateConfigs->supportsFrameRateOverrideByContent(); + leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); @@ -282,7 +282,7 @@ void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDis thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); } -void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { +void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { { std::lock_guard<std::mutex> lock(mPolicyLock); // Cache the last reported modes for primary display. @@ -297,7 +297,7 @@ void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayMode void Scheduler::dispatchCachedReportedMode() { // Check optional fields first. - if (!mPolicy.mode) { + if (!mPolicy.modeOpt) { ALOGW("No mode ID found, not dispatching cached mode."); return; } @@ -309,22 +309,21 @@ void Scheduler::dispatchCachedReportedMode() { // If the mode is not the current mode, this means that a // mode change is in progress. In that case we shouldn't dispatch an event // as it will be dispatched when the current mode changes. - if (std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->getActiveMode() != mPolicy.mode) { + if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) { return; } // If there is no change from cached mode, there is no need to dispatch an event - if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) { + if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) { return; } - mPolicy.cachedModeChangedParams->mode = mPolicy.mode; + mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt; onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle, mPolicy.cachedModeChangedParams->mode); } -void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { +void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { android::EventThread* thread; { std::lock_guard<std::mutex> lock(mConnectionsLock); @@ -361,50 +360,6 @@ void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds wo thread->setDuration(workDuration, readyDuration); } -DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) { - const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now); - const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod(); - return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod}; -} - -ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { - if (mInjectVSyncs == enable) { - return {}; - } - - ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling"); - - if (!mInjectorConnectionHandle) { - auto vsyncSource = std::make_unique<InjectVSyncSource>(); - mVSyncInjector = vsyncSource.get(); - - auto eventThread = - std::make_unique<impl::EventThread>(std::move(vsyncSource), - /*tokenManager=*/nullptr, - impl::EventThread::InterceptVSyncsCallback(), - impl::EventThread::ThrottleVsyncCallback(), - impl::EventThread::GetVsyncPeriodFunction()); - - // EventThread does not dispatch VSYNC unless the display is connected and powered on. - eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true); - eventThread->onScreenAcquired(); - - mInjectorConnectionHandle = createConnection(std::move(eventThread)); - } - - mInjectVSyncs = enable; - return mInjectorConnectionHandle; -} - -bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) { - if (!mInjectVSyncs || !mVSyncInjector) { - return false; - } - - mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp); - return true; -} - void Scheduler::enableHardwareVsync() { std::lock_guard<std::mutex> lock(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { @@ -440,6 +395,24 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) { setVsyncPeriod(refreshRate.getPeriodNsecs()); } +void Scheduler::setRenderRate(Fps renderFrameRate) { + const auto mode = leaderSelectorPtr()->getActiveMode(); + + using fps_approx_ops::operator!=; + LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps, + "Mismatch in render frame rates. Selector: %s, Scheduler: %s", + to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str()); + + ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), + to_string(mode.modePtr->getFps()).c_str()); + + const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps); + LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(), + to_string(mode.fps).c_str()); + + mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor)); +} + void Scheduler::resync() { static constexpr nsecs_t kIgnoreDelay = ms2ns(750); @@ -447,10 +420,7 @@ void Scheduler::resync() { const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - const auto refreshRate = [&] { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveMode()->getFps(); - }(); + const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); resyncToHardwareVsync(false, refreshRate); } } @@ -497,24 +467,10 @@ void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) { } void Scheduler::registerLayer(Layer* layer) { - using WindowType = gui::WindowInfo::Type; - - scheduler::LayerHistory::LayerVoteType voteType; - - if (!mFeatures.test(Feature::kContentDetection) || - layer->getWindowType() == WindowType::STATUS_BAR) { - voteType = scheduler::LayerHistory::LayerVoteType::NoVote; - } else if (layer->getWindowType() == WindowType::WALLPAPER) { - // Running Wallpaper at Min is considered as part of content detection. - voteType = scheduler::LayerHistory::LayerVoteType::Min; - } else { - voteType = scheduler::LayerHistory::LayerVoteType::Heuristic; - } - // If the content detection feature is off, we still keep the layer history, // since we use it for other features (like Frame Rate API), so layers // still need to be registered. - mLayerHistory.registerLayer(layer, voteType); + mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection)); } void Scheduler::deregisterLayer(Layer* layer) { @@ -523,39 +479,38 @@ void Scheduler::deregisterLayer(Layer* layer) { void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) { - { - std::scoped_lock lock(mRefreshRateConfigsLock); - if (!mRefreshRateConfigs->canSwitch()) return; + if (leaderSelectorPtr()->canSwitch()) { + mLayerHistory.record(layer, presentTime, systemTime(), updateType); } - - mLayerHistory.record(layer, presentTime, systemTime(), updateType); } void Scheduler::setModeChangePending(bool pending) { mLayerHistory.setModeChangePending(pending); } +void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) { + mLayerHistory.setDefaultFrameRateCompatibility(layer, + mFeatures.test(Feature::kContentDetection)); +} + void Scheduler::chooseRefreshRateForContent() { - const auto configs = holdRefreshRateConfigs(); - if (!configs->canSwitch()) return; + const auto selectorPtr = leaderSelectorPtr(); + if (!selectorPtr->canSwitch()) return; ATRACE_CALL(); - LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime()); + LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime()); applyPolicy(&Policy::contentRequirements, std::move(summary)); } void Scheduler::resetIdleTimer() { - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false); + leaderSelectorPtr()->resetIdleTimer(); } void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); - - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true); + leaderSelectorPtr()->resetKernelIdleTimer(); } } @@ -580,10 +535,7 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number - const Fps refreshRate = [&] { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveMode()->getFps(); - }(); + const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz; using namespace fps_approx_ops; @@ -625,22 +577,40 @@ void Scheduler::displayPowerTimerCallback(TimerState state) { ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } -void Scheduler::dump(std::string& result) const { - using base::StringAppendF; +void Scheduler::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; - StringAppendF(&result, "+ Touch timer: %s\n", - mTouchTimer ? mTouchTimer->dump().c_str() : "off"); - StringAppendF(&result, "+ Content detection: %s %s\n\n", - mFeatures.test(Feature::kContentDetection) ? "on" : "off", - mLayerHistory.dump().c_str()); + { + utils::Dumper::Section section(dumper, "Features"sv); - mFrameRateOverrideMappings.dump(result); + for (Feature feature : ftl::enum_range<Feature>()) { + if (const auto flagOpt = ftl::flag_name(feature)) { + dumper.dump(flagOpt->substr(1), mFeatures.test(feature)); + } + } + } + { + utils::Dumper::Section section(dumper, "Policy"sv); + { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + dumper.dump("leaderDisplayId"sv, mLeaderDisplayId); + } + dumper.dump("layerHistory"sv, mLayerHistory.dump()); + dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval)); + dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval)); + } + + mFrameRateOverrideMappings.dump(dumper); + dumper.eol(); { + utils::Dumper::Section section(dumper, "Hardware VSYNC"sv); + std::lock_guard lock(mHWVsyncLock); - StringAppendF(&result, - "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n", - mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable); + dumper.dump("screenAcquired"sv, mScreenAcquired.load()); + dumper.dump("hwVsyncAvailable"sv, mHWVsyncAvailable); + dumper.dump("hwVsyncEnabled"sv, mPrimaryHWVsyncEnabled); } } @@ -649,54 +619,101 @@ void Scheduler::dumpVsync(std::string& out) const { } bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { - const auto refreshRateConfigs = holdRefreshRateConfigs(); + if (consideredSignals.idle) return false; + + const auto frameRateOverrides = + leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements, + displayRefreshRate, consideredSignals); + + // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying + // the FrameRateOverrideMappings rather than here. + return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); +} - // we always update mFrameRateOverridesByContent here - // supportsFrameRateOverridesByContent will be checked - // when getting FrameRateOverrides from mFrameRateOverrideMappings - if (!consideredSignals.idle) { - const auto frameRateOverrides = - refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements, - displayRefreshRate, consideredSignals); - return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); +void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) { + // TODO(b/241286431): Choose the leader display. + mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first); + ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str()); + + if (const auto leaderPtr = leaderSelectorPtrLocked()) { + leaderPtr->setIdleTimerCallbacks( + {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, + .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, + .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, + .onExpired = + [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); + + leaderPtr->startIdleTimer(); } - return false; +} + +void Scheduler::demoteLeaderDisplay() { + // No need to lock for reads on kMainThreadContext. + if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) { + leaderPtr->stopIdleTimer(); + leaderPtr->clearIdleTimerCallbacks(); + } + + // Clear state that depends on the leader's RefreshRateSelector. + std::scoped_lock lock(mPolicyLock); + mPolicy = {}; } template <typename S, typename T> auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals { - DisplayModePtr newMode; + std::vector<display::DisplayModeRequest> modeRequests; GlobalSignals consideredSignals; bool refreshRateChanged = false; bool frameRateOverridesChanged; - const auto refreshRateConfigs = holdRefreshRateConfigs(); { - std::lock_guard<std::mutex> lock(mPolicyLock); + std::scoped_lock lock(mPolicyLock); auto& currentState = mPolicy.*statePtr; if (currentState == newState) return {}; currentState = std::forward<T>(newState); - std::tie(newMode, consideredSignals) = chooseDisplayMode(); - frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); + DisplayModeChoiceMap modeChoices; + ftl::Optional<FrameRateMode> modeOpt; + { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + + modeChoices = chooseDisplayModes(); + + // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest + // to go through. Fix this by tracking per-display Scheduler::Policy and timers. + std::tie(modeOpt, consideredSignals) = + modeChoices.get(*mLeaderDisplayId) + .transform([](const DisplayModeChoice& choice) { + return std::make_pair(choice.mode, choice.consideredSignals); + }) + .value(); + } - if (mPolicy.mode == newMode) { + modeRequests.reserve(modeChoices.size()); + for (auto& [id, choice] : modeChoices) { + modeRequests.emplace_back( + display::DisplayModeRequest{.mode = std::move(choice.mode), + .emitEvent = !choice.consideredSignals.idle}); + } + + frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps); + + if (mPolicy.modeOpt != modeOpt) { + mPolicy.modeOpt = modeOpt; + refreshRateChanged = true; + } else { // We don't need to change the display mode, but we might need to send an event // about a mode change, since it was suppressed if previously considered idle. if (!consideredSignals.idle) { dispatchCachedReportedMode(); } - } else { - mPolicy.mode = newMode; - refreshRateChanged = true; } } if (refreshRateChanged) { - mSchedulerCallback.requestDisplayMode(std::move(newMode), - consideredSignals.idle ? DisplayModeEvent::None - : DisplayModeEvent::Changed); + mSchedulerCallback.requestDisplayModes(std::move(modeRequests)); } if (frameRateOverridesChanged) { mSchedulerCallback.triggerOnFrameRateOverridesChanged(); @@ -704,33 +721,112 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals return consideredSignals; } -auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> { +auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { ATRACE_CALL(); - const auto configs = holdRefreshRateConfigs(); + using RankedRefreshRates = RefreshRateSelector::RankedFrameRates; + display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking; + + // Tallies the score of a refresh rate across `displayCount` displays. + struct RefreshRateTally { + explicit RefreshRateTally(float score) : score(score) {} + + float score; + size_t displayCount = 1; + }; + + // Chosen to exceed a typical number of refresh rates across displays. + constexpr size_t kStaticCapacity = 8; + ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies; - // If Display Power is not in normal operation we want to be in performance mode. When coming - // back to normal mode, a grace period is given with DisplayPowerTimer. - if (mDisplayPowerTimer && - (mPolicy.displayPowerMode != hal::PowerMode::ON || - mPolicy.displayPowerTimer == TimerState::Reset)) { - constexpr GlobalSignals kNoSignals; - return {configs->getMaxRefreshRateByPolicy(), kNoSignals}; + const auto globalSignals = makeGlobalSignals(); + + for (const auto& [id, selectorPtr] : mRefreshRateSelectors) { + auto rankedFrameRates = + selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals); + + for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) { + const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score); + + if (!inserted) { + auto& tally = it->second; + tally.score += score; + tally.displayCount++; + } + } + + perDisplayRanking.push_back(std::move(rankedFrameRates)); + } + + auto maxScoreIt = refreshRateTallies.cbegin(); + + // Find the first refresh rate common to all displays. + while (maxScoreIt != refreshRateTallies.cend() && + maxScoreIt->second.displayCount != mRefreshRateSelectors.size()) { + ++maxScoreIt; + } + + if (maxScoreIt != refreshRateTallies.cend()) { + // Choose the highest refresh rate common to all displays, if any. + for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) { + const auto [fps, tally] = *it; + + if (tally.displayCount == mRefreshRateSelectors.size() && + tally.score > maxScoreIt->second.score) { + maxScoreIt = it; + } + } } - const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active, - .idle = mPolicy.idleTimer == TimerState::Expired}; + const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend() + ? std::make_optional(maxScoreIt->first) + : std::nullopt; - return configs->getBestRefreshRate(mPolicy.contentRequirements, signals); + DisplayModeChoiceMap modeChoices; + + using fps_approx_ops::operator==; + + for (auto& [ranking, signals] : perDisplayRanking) { + if (!chosenFps) { + const auto& [frameRateMode, _] = ranking.front(); + modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(), + DisplayModeChoice{frameRateMode, signals}); + continue; + } + + for (auto& [frameRateMode, _] : ranking) { + if (frameRateMode.fps == *chosenFps) { + modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(), + DisplayModeChoice{frameRateMode, signals}); + break; + } + } + } + return modeChoices; } -DisplayModePtr Scheduler::getPreferredDisplayMode() { +GlobalSignals Scheduler::makeGlobalSignals() const { + const bool powerOnImminent = mDisplayPowerTimer && + (mPolicy.displayPowerMode != hal::PowerMode::ON || + mPolicy.displayPowerTimer == TimerState::Reset); + + return {.touch = mTouchTimer && mPolicy.touch == TouchState::Active, + .idle = mPolicy.idleTimer == TimerState::Expired, + .powerOnImminent = powerOnImminent}; +} + +ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mPolicyLock); // Make sure the stored mode is up to date. - if (mPolicy.mode) { - mPolicy.mode = chooseDisplayMode().first; + if (mPolicy.modeOpt) { + const auto ranking = + leaderSelectorPtr() + ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals()) + .ranking; + + mPolicy.modeOpt = ranking.front().frameRateMode; } - return mPolicy.mode; + return mPolicy.modeOpt; } void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) { @@ -776,11 +872,4 @@ void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverrid mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride); } -std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom( - nsecs_t expectedPresentTime) const { - const auto presentTime = std::chrono::nanoseconds(expectedPresentTime); - const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod()); - return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod); -} - } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a8043bf94c..cf2ffb8cdd 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -22,8 +22,8 @@ #include <future> #include <memory> #include <mutex> -#include <optional> #include <unordered_map> +#include <utility> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -32,14 +32,21 @@ #include <ui/GraphicTypes.h> #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" +#include <ftl/fake_guard.h> +#include <ftl/optional.h> #include <scheduler/Features.h> +#include <scheduler/Time.h> +#include <ui/DisplayId.h> +#include "Display/DisplayMap.h" +#include "Display/DisplayModeRequest.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "LayerHistory.h" #include "MessageQueue.h" #include "OneShotTimer.h" -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" +#include "Utils/Dumper.h" #include "VsyncSchedule.h" namespace android::scheduler { @@ -74,7 +81,6 @@ struct hash<android::scheduler::ConnectionHandle> { namespace android { class FenceTime; -class InjectVSyncSource; namespace frametimeline { class TokenManager; @@ -82,11 +88,11 @@ class TokenManager; namespace scheduler { -struct ISchedulerCallback { - using DisplayModeEvent = scheduler::DisplayModeEvent; +using GlobalSignals = RefreshRateSelector::GlobalSignals; +struct ISchedulerCallback { virtual void setVsyncEnabled(bool) = 0; - virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0; + virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0; virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; @@ -94,27 +100,35 @@ protected: ~ISchedulerCallback() = default; }; -class Scheduler : impl::MessageQueue { - using Impl = impl::MessageQueue; +class Scheduler : android::impl::MessageQueue { + using Impl = android::impl::MessageQueue; public: Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags); virtual ~Scheduler(); void startTimers(); - void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>) - EXCLUDES(mRefreshRateConfigsLock); + + // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}. + void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + + using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; + + void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); void run(); void createVsyncSchedule(FeatureFlags); using Impl::initVsync; - using Impl::setInjector; using Impl::getScheduledFrameTime; using Impl::setDuration; + using Impl::scheduleConfigure; using Impl::scheduleFrame; // Schedule an asynchronous or synchronous task on the main thread. @@ -127,17 +141,16 @@ public: ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*, std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration, - impl::EventThread::InterceptVSyncsCallback); + std::chrono::nanoseconds readyDuration); sp<IDisplayEventConnection> createDisplayEventConnection( - ConnectionHandle, ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); + ConnectionHandle, EventRegistrationFlags eventRegistration = {}); sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock); - void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr); + void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock); + void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); @@ -148,12 +161,9 @@ public: void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); - DisplayStatInfo getDisplayStatInfo(nsecs_t now); + // Sets the render rate for the scheduler to run at. + void setRenderRate(Fps); - // Returns injector handle if injection has toggled, or an invalid handle otherwise. - ConnectionHandle enableVSyncInjection(bool enable); - // Returns false if injection is disabled. - bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp); void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); @@ -162,7 +172,7 @@ public: // Otherwise, if hardware vsync is not already enabled then this method will // no-op. void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate); - void resync() EXCLUDES(mRefreshRateConfigsLock); + void resync() EXCLUDES(mDisplayLock); void forceNextResync() { mLastResyncTime = 0; } // Passes a vsync sample to VsyncController. periodFlushed will be true if @@ -173,13 +183,14 @@ public: // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); - void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) - EXCLUDES(mRefreshRateConfigsLock); + void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType) + EXCLUDES(mDisplayLock); void setModeChangePending(bool pending); + void setDefaultFrameRateCompatibility(Layer*); void deregisterLayer(Layer*); // Detects content using layer history, and selects a matching refresh rate. - void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock); + void chooseRefreshRateForContent() EXCLUDES(mDisplayLock); void resetIdleTimer(); @@ -188,20 +199,18 @@ public: void setDisplayPowerMode(hal::PowerMode powerMode); - VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); } + VsyncSchedule& getVsyncSchedule() { return *mVsyncSchedule; } // Returns true if a given vsync timestamp is considered valid vsync // for a given uid - bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const; - - std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const; + bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const; - void dump(std::string&) const; + void dump(utils::Dumper&) const; void dump(ConnectionHandle, std::string&) const; void dumpVsync(std::string&) const; // Get the appropriate refresh for current conditions. - DisplayModePtr getPreferredDisplayMode(); + ftl::Optional<FrameRateMode> getPreferredDisplayMode(); // Notifies the scheduler about a refresh rate timeline change. void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline); @@ -226,11 +235,10 @@ public: void setGameModeRefreshRateForUid(FrameRateOverride); // Retrieves the overridden refresh rate for a given uid. - std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock); + std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock); - nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveMode()->getFps().getPeriodNsecs(); + nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) { + return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs(); } // Returns the framerate of the layer with the given sequence ID @@ -245,20 +253,30 @@ private: enum class TimerState { Reset, Expired }; enum class TouchState { Inactive, Active }; + // impl::MessageQueue overrides: + void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override; + // Create a connection on the given EventThread. ConnectionHandle createConnection(std::unique_ptr<EventThread>); sp<EventThreadConnection> createConnectionInternal( - EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); + EventThread*, EventRegistrationFlags eventRegistration = {}); // Update feature state machine to given state when corresponding timer resets or expires. - void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock); + void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock); void idleTimerCallback(TimerState); void touchTimerCallback(TimerState); void displayPowerTimerCallback(TimerState); void setVsyncPeriod(nsecs_t period); - using GlobalSignals = RefreshRateConfigs::GlobalSignals; + // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new + // `mLeaderDisplayId` is never `std::nullopt`. + void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt) + REQUIRES(kMainThreadContext, mDisplayLock); + + // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the + // caller on the main thread to avoid deadlock, since the timer thread locks it before exit. + void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock); struct Policy; @@ -267,22 +285,38 @@ private: template <typename S, typename T> GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock); - // Returns the display mode that fulfills the policy, and the signals that were considered. - std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock); + struct DisplayModeChoice { + DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals) + : mode(std::move(mode)), consideredSignals(consideredSignals) {} - bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); + FrameRateMode mode; + GlobalSignals consideredSignals; + + bool operator==(const DisplayModeChoice& other) const { + return mode == other.mode && consideredSignals == other.consideredSignals; + } - void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock); + // For tests. + friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) { + return stream << '{' << to_string(*choice.mode.modePtr) << " considering " + << choice.consideredSignals.toString().c_str() << '}'; + } + }; - impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const - EXCLUDES(mRefreshRateConfigsLock); - impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; + using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>; - std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const - EXCLUDES(mRefreshRateConfigsLock) { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs; - } + // See mDisplayLock for thread safety. + DisplayModeChoiceMap chooseDisplayModes() const + REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext); + + GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock); + + bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); + + void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock); + + android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const; + android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { @@ -294,10 +328,6 @@ private: mutable std::mutex mConnectionsLock; std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock); - bool mInjectVSyncs = false; - InjectVSyncSource* mVSyncInjector = nullptr; - ConnectionHandle mInjectorConnectionHandle; - mutable std::mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false; bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false; @@ -311,14 +341,41 @@ private: LayerHistory mLayerHistory; // Timer used to monitor touch events. - std::optional<OneShotTimer> mTouchTimer; + ftl::Optional<OneShotTimer> mTouchTimer; // Timer used to monitor display power mode. - std::optional<OneShotTimer> mDisplayPowerTimer; + ftl::Optional<OneShotTimer> mDisplayPowerTimer; ISchedulerCallback& mSchedulerCallback; + // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; + // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so + // must lock for writes but not reads. See also mPolicyLock for locking order. + mutable std::mutex mDisplayLock; + + display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors + GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext); + + ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock) + GUARDED_BY(kMainThreadContext); + + RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) { + std::scoped_lock lock(mDisplayLock); + return leaderSelectorPtrLocked(); + } + + RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) { + ftl::FakeGuard guard(kMainThreadContext); + const RefreshRateSelectorPtr noLeader; + return mLeaderDisplayId + .and_then([this](PhysicalDisplayId leaderId) + REQUIRES(mDisplayLock, kMainThreadContext) { + return mRefreshRateSelectors.get(leaderId); + }) + .value_or(std::cref(noLeader)); + } + struct Policy { // Policy for choosing the display mode. LayerHistory::Summary contentRequirements; @@ -328,20 +385,17 @@ private: hal::PowerMode displayPowerMode = hal::PowerMode::ON; // Chosen display mode. - DisplayModePtr mode; + ftl::Optional<FrameRateMode> modeOpt; struct ModeChangedParams { ConnectionHandle handle; - DisplayModePtr mode; + FrameRateMode mode; }; // Parameters for latest dispatch of mode change event. std::optional<ModeChangedParams> cachedModeChangedParams; } mPolicy GUARDED_BY(mPolicyLock); - mutable std::mutex mRefreshRateConfigsLock; - std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock); - std::mutex mVsyncTimelineLock; std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline GUARDED_BY(mVsyncTimelineLock); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 27f43115c1..cc9f7cfaaa 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -100,14 +100,8 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim return getExpectedCallbackTime(nextVsyncTime, timing); } - bool const alreadyDispatchedForVsync = mLastDispatchTime && - ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime && - (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime); - if (alreadyDispatchedForVsync) { - nextVsyncTime = - tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance); - nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; - } + nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); + nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; auto const nextReadyTime = nextVsyncTime - timing.readyDuration; mScheduleTiming = timing; @@ -123,6 +117,25 @@ bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const { return mWorkloadUpdateInfo.has_value(); } +nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, + nsecs_t nextVsyncTime) const { + bool const alreadyDispatchedForVsync = mLastDispatchTime && + ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime && + (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime); + const nsecs_t currentPeriod = tracker.currentPeriod(); + bool const nextVsyncTooClose = mLastDispatchTime && + (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod; + if (alreadyDispatchedForVsync) { + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance); + } + + if (nextVsyncTooClose) { + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod); + } + + return nextVsyncTime; +} + void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo && !mWorkloadUpdateInfo) { return; @@ -136,7 +149,9 @@ void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration; const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync); - const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync); + const auto nextVsyncTime = + adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/ + tracker.nextAnticipatedVSyncTimeFrom(earliestVsync)); const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration; const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index 4923031098..4f2f87a7d2 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -84,6 +84,8 @@ public: void dump(std::string& result) const; private: + nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const; + const std::string mName; const VSyncDispatch::Callback mCallback; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 77782e9c72..ed4d25e49b 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -34,7 +34,7 @@ #include <utils/Log.h> #include <utils/Trace.h> -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" #include "VSyncPredictor.h" namespace android::scheduler { @@ -167,7 +167,9 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { vsyncTS[i] = timestamp; meanTS += timestamp; - const auto ordinal = (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor; + const auto ordinal = currentPeriod == 0 + ? 0 + : (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor; ordinals[i] = ordinal; meanOrdinal += ordinal; } @@ -251,7 +253,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { std::lock_guard lock(mMutex); - return nextAnticipatedVSyncTimeFromLocked(timePoint); + + // TODO(b/246164114): This implementation is not efficient at all. Refactor. + nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint); + while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) { + nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1); + } + return nextVsync; } /* @@ -263,6 +271,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { * isVSyncInPhase(50.0, 30) = true */ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { + std::lock_guard lock(mMutex); + const auto divisor = + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); + return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor)); +} + +bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const { struct VsyncError { nsecs_t vsyncTimestamp; float error; @@ -270,9 +285,6 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { bool operator<(const VsyncError& other) const { return error < other.error; } }; - std::lock_guard lock(mMutex); - const auto divisor = - RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); if (divisor <= 1 || timePoint == 0) { return true; } @@ -310,6 +322,12 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2; } +void VSyncPredictor::setDivisor(unsigned divisor) { + ALOGV("%s: %d", __func__, divisor); + std::lock_guard lock(mMutex); + mDivisor = divisor; +} + VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { std::lock_guard lock(mMutex); const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 3181102663..4a3ba67419 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -67,6 +67,8 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); + void setDivisor(unsigned divisor) final EXCLUDES(mMutex); + void dump(std::string& result) const final EXCLUDES(mMutex); private: @@ -89,6 +91,8 @@ private: nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); + bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); + nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); @@ -100,6 +104,8 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); + + unsigned mDivisor GUARDED_BY(mMutex) = 1; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 76315d2b5b..8d1629faae 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -79,6 +79,17 @@ public: */ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; + /* + * Sets a divisor on the rate (which is a multiplier of the period). + * The tracker will continue to track the vsync timeline and expect it + * to match the current period, however, nextAnticipatedVSyncTimeFrom will + * return vsyncs according to the divisor set. Setting a divisor is useful + * when a display is running at 120Hz but the render frame rate is 60Hz. + * + * \param [in] divisor The rate divisor the tracker should operate at. + */ + virtual void setDivisor(unsigned divisor) = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp index be57b2acd7..138d8d65f7 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -53,14 +53,14 @@ VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(Transactio case Schedule::EarlyStart: if (token) { mEarlyWakeupRequests.emplace(token); - token->linkToDeath(this); + token->linkToDeath(sp<DeathRecipient>::fromExisting(this)); } else { ALOGW("%s: EarlyStart requested without a valid token", __func__); } break; case Schedule::EarlyEnd: { if (token && mEarlyWakeupRequests.erase(token) > 0) { - token->unlinkToDeath(this); + token->unlinkToDeath(sp<DeathRecipient>::fromExisting(this)); } else { ALOGW("%s: Unexpected EarlyEnd", __func__); } diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 3a918a1660..95bc31f239 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -68,6 +68,14 @@ VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, Controlle VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default; VsyncSchedule::~VsyncSchedule() = default; +Period VsyncSchedule::period() const { + return Period::fromNs(mTracker->currentPeriod()); +} + +TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const { + return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns())); +} + void VsyncSchedule::dump(std::string& out) const { out.append("VsyncController:\n"); mController->dump(out); diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 0d9b114875..8c17409b02 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -20,6 +20,7 @@ #include <string> #include <scheduler/Features.h> +#include <scheduler/Time.h> namespace android::scheduler { @@ -38,6 +39,9 @@ public: VsyncSchedule(VsyncSchedule&&); ~VsyncSchedule(); + Period period() const; + TimePoint vsyncDeadlineAfter(TimePoint) const; + // TODO(b/185535769): Hide behind API. const VsyncTracker& getTracker() const { return *mTracker; } VsyncTracker& getTracker() { return *mTracker; } diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index bd4f40989d..5522ff80c8 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -66,6 +66,18 @@ struct FpsRange { Fps max = Fps::fromValue(std::numeric_limits<float>::max()); bool includes(Fps) const; + bool includes(FpsRange) const; +}; + +struct FpsRanges { + // The range of refresh rates that refers to the display mode setting. + FpsRange physical; + + // the range of frame rates that refers to the render rate, which is + // the rate that frames are swapped. + FpsRange render; + + bool valid() const; }; static_assert(std::is_trivially_copyable_v<Fps>); @@ -127,13 +139,39 @@ inline bool operator!=(FpsRange lhs, FpsRange rhs) { return !(lhs == rhs); } +inline bool operator==(const FpsRanges& lhs, const FpsRanges& rhs) { + return lhs.physical == rhs.physical && lhs.render == rhs.render; +} + +inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) { + return !(lhs == rhs); +} + +inline unsigned operator/(Fps lhs, Fps rhs) { + return static_cast<unsigned>(std::ceil(lhs.getValue() / rhs.getValue())); +} + } // namespace fps_approx_ops +constexpr Fps operator/(Fps fps, unsigned divisor) { + return Fps::fromPeriodNsecs(fps.getPeriodNsecs() * static_cast<nsecs_t>(divisor)); +} + inline bool FpsRange::includes(Fps fps) const { using fps_approx_ops::operator<=; return min <= fps && fps <= max; } +inline bool FpsRange::includes(FpsRange range) const { + using namespace fps_approx_ops; + return min <= range.min && max >= range.max; +} + +inline bool FpsRanges::valid() const { + using fps_approx_ops::operator>=; + return physical.max >= render.max; +} + struct FpsApproxEqual { bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); } }; @@ -151,4 +189,10 @@ inline std::string to_string(FpsRange range) { return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str()); } +inline std::string to_string(FpsRanges ranges) { + const auto& [physical, render] = ranges; + return base::StringPrintf("{physical=%s, render=%s}", to_string(physical).c_str(), + to_string(render).c_str()); +} + } // namespace android diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h new file mode 100644 index 0000000000..db38ebe658 --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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 <ftl/non_null.h> +#include <scheduler/Fps.h> + +// TODO(b/241285191): Pull this to <ui/DisplayMode.h> +#include "DisplayHardware/DisplayMode.h" + +namespace android::scheduler { + +struct FrameRateMode { + Fps fps; // The render frame rate, which is a divisor of modePtr->getFps(). + ftl::NonNull<DisplayModePtr> modePtr; + + bool operator==(const FrameRateMode& other) const { + return isApproxEqual(fps, other.fps) && modePtr == other.modePtr; + } + + bool operator!=(const FrameRateMode& other) const { return !(*this == other); } +}; + +inline std::string to_string(const FrameRateMode& mode) { + return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")"; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h b/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h new file mode 100644 index 0000000000..23ae83fc4b --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h @@ -0,0 +1,54 @@ +/* + * Copyright 2022 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 <memory> +#include <queue> +#include <utility> + +#include <scheduler/Time.h> + +namespace android { + +class FenceTime; + +namespace scheduler { + +// Computes composite-to-present latency by tracking recently composited frames pending to present. +class PresentLatencyTracker { +public: + // For tests. + static constexpr size_t kMaxPendingFrames = 4; + + // Returns the present latency of the latest frame. + Duration trackPendingFrame(TimePoint compositeTime, + std::shared_ptr<FenceTime> presentFenceTime); + +private: + struct PendingFrame { + PendingFrame(TimePoint compositeTime, std::shared_ptr<FenceTime> presentFenceTime) + : compositeTime(compositeTime), presentFenceTime(std::move(presentFenceTime)) {} + + const TimePoint compositeTime; + const std::shared_ptr<FenceTime> presentFenceTime; + }; + + std::queue<PendingFrame> mPendingFrames; +}; + +} // namespace scheduler +} // namespace android diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h new file mode 100644 index 0000000000..bd4e3c27b3 --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h @@ -0,0 +1,88 @@ +/* + * Copyright 2022 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 <string> + +#include <android-base/stringprintf.h> +#include <utils/Timers.h> + +namespace android { +namespace scheduler { + +// TODO(b/185535769): Pull Clock.h to libscheduler to reuse this. +using SchedulerClock = std::chrono::high_resolution_clock; +static_assert(SchedulerClock::is_steady); + +} // namespace scheduler + +struct Duration; + +struct TimePoint : scheduler::SchedulerClock::time_point { + constexpr TimePoint() = default; + explicit constexpr TimePoint(const Duration&); + + // Implicit conversion from std::chrono counterpart. + constexpr TimePoint(scheduler::SchedulerClock::time_point p) + : scheduler::SchedulerClock::time_point(p) {} + + static constexpr TimePoint fromNs(nsecs_t); + + static TimePoint now() { return scheduler::SchedulerClock::now(); }; + + nsecs_t ns() const; +}; + +struct Duration : TimePoint::duration { + // Implicit conversion from std::chrono counterpart. + template <typename R, typename P> + constexpr Duration(std::chrono::duration<R, P> d) : TimePoint::duration(d) {} + + static constexpr Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; } + + nsecs_t ns() const { return std::chrono::nanoseconds(*this).count(); } +}; + +using Period = Duration; + +constexpr TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {} + +constexpr TimePoint TimePoint::fromNs(nsecs_t ns) { + return TimePoint(Duration::fromNs(ns)); +} + +inline nsecs_t TimePoint::ns() const { + return Duration(time_since_epoch()).ns(); +} + +// Shorthand to convert the tick count of a Duration to Period and Rep. For example: +// +// const auto i = ticks<std::ratio<1>>(d); // Integer seconds. +// const auto f = ticks<std::milli, float>(d); // Floating-point milliseconds. +// +template <typename Period, typename Rep = Duration::rep> +constexpr Rep ticks(Duration d) { + using D = std::chrono::duration<Rep, Period>; + return std::chrono::duration_cast<D>(d).count(); +} + +inline std::string to_string(Duration d) { + return base::StringPrintf("%.3f ms", ticks<std::milli, float>(d)); +} + +} // namespace android diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h index e30e9072fc..c64a3cdc6f 100644 --- a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl +++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2022 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. @@ -14,19 +14,21 @@ * limitations under the License. */ -package android.content.pm; +#pragma once -/** - * This event is designed for notification to native code listener about - * any changes on a package including update, deletion and etc. - * - * @hide - */ -parcelable PackageChangeEvent { - @utf8InCpp String packageName; - long version; - long lastUpdateTimeMillis; - boolean newInstalled; - boolean dataRemoved; - boolean isDeleted; +#include <cstdint> + +namespace android { + +// TODO(b/185536303): Import StrongTyping.h into FTL so it can be used here. + +// Sequential frame identifier, also known as FrameTimeline token. +struct VsyncId { + int64_t value = -1; +}; + +inline bool operator==(VsyncId lhs, VsyncId rhs) { + return lhs.value == rhs.value; } + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp b/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp new file mode 100644 index 0000000000..8f3e0818f8 --- /dev/null +++ b/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 <scheduler/PresentLatencyTracker.h> + +#include <cutils/compiler.h> +#include <log/log.h> +#include <ui/FenceTime.h> + +namespace android::scheduler { + +Duration PresentLatencyTracker::trackPendingFrame(TimePoint compositeTime, + std::shared_ptr<FenceTime> presentFenceTime) { + Duration presentLatency = Duration::zero(); + while (!mPendingFrames.empty()) { + const auto& pendingFrame = mPendingFrames.front(); + const auto presentTime = + TimePoint::fromNs(pendingFrame.presentFenceTime->getCachedSignalTime()); + + if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_PENDING)) { + break; + } + + if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_INVALID)) { + ALOGE("%s: Invalid present fence", __func__); + } else { + presentLatency = presentTime - pendingFrame.compositeTime; + } + + mPendingFrames.pop(); + } + + mPendingFrames.emplace(compositeTime, std::move(presentFenceTime)); + + if (CC_UNLIKELY(mPendingFrames.size() > kMaxPendingFrames)) { + ALOGE("%s: Too many pending frames", __func__); + mPendingFrames.pop(); + } + + return presentLatency; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp new file mode 100644 index 0000000000..8952ca99ab --- /dev/null +++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2022 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 <algorithm> +#include <array> + +#include <scheduler/PresentLatencyTracker.h> +#include <ui/FenceTime.h> + +namespace android::scheduler { +namespace { + +using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>; + +FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) { + const auto fence = sp<Fence>::make(); + return {fence, fenceMap.createFenceTimeForTest(fence)}; +} + +} // namespace + +TEST(PresentLatencyTrackerTest, skipsInvalidFences) { + PresentLatencyTracker tracker; + + const TimePoint kCompositeTime = TimePoint::fromNs(999); + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero()); + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero()); + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero()); + + FenceToFenceTimeMap fenceMap; + const auto [fence, fenceTime] = makePendingFence(fenceMap); + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero()); + + fenceTime->signalForTest(9999); + + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), + Duration::fromNs(9000)); +} + +TEST(PresentLatencyTrackerTest, tracksPendingFrames) { + PresentLatencyTracker tracker; + + FenceToFenceTimeMap fenceMap; + std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences; + std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); }); + + // The present latency is 0 if all fences are pending. + const TimePoint kCompositeTime = TimePoint::fromNs(1234); + for (const auto& [fence, fenceTime] : fences) { + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero()); + } + + // If multiple frames have been presented... + constexpr size_t kPresentCount = fences.size() / 2; + for (size_t i = 0; i < kPresentCount; i++) { + fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i)); + } + + const auto fence = makePendingFence(fenceMap); + + // ...then the present latency is measured using the latest frame. + constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1); + EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fence.second), kPresentLatency); +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp new file mode 100644 index 0000000000..37b3218138 --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2022 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 "ScreenCaptureOutput.h" +#include "ScreenCaptureRenderSurface.h" + +#include <compositionengine/CompositionEngine.h> +#include <compositionengine/DisplayColorProfileCreationArgs.h> +#include <compositionengine/impl/DisplayColorProfile.h> +#include <ui/Rotation.h> + +namespace android { + +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) { + std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< + ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, + std::unordered_set<compositionengine::LayerFE*>, + const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine, + args.renderArea, + std::move( + args.filterForScreenshot), + args.colorProfile, + args.regionSampling); + output->editState().isSecure = args.renderArea.isSecure(); + output->setCompositionEnabled(true); + output->setLayerFilter({args.layerStack}); + output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer))); + output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits); + + output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>( + compositionengine::DisplayColorProfileCreationArgsBuilder() + .setHasWideColorGamut(true) + .Build())); + + ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags()); + Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(), args.renderArea.getReqHeight()}; + output->setProjection(orientation, args.renderArea.getLayerStackSpaceRect(), + orientedDisplaySpaceRect); + + Rect sourceCrop = args.renderArea.getSourceCrop(); + output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()}); + + return output; +} + +ScreenCaptureOutput::ScreenCaptureOutput( + const RenderArea& renderArea, + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot, + const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling) + : mRenderArea(renderArea), + mFilterForScreenshot(std::move(filterForScreenshot)), + mColorProfile(colorProfile), + mRegionSampling(regionSampling) {} + +void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { + auto& outputState = editState(); + outputState.dataspace = mColorProfile.dataspace; + outputState.renderIntent = mColorProfile.renderIntent; +} + +renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings() + const { + auto clientCompositionDisplay = + compositionengine::impl::Output::generateClientCompositionDisplaySettings(); + clientCompositionDisplay.clip = mRenderArea.getSourceCrop(); + clientCompositionDisplay.targetLuminanceNits = -1; + return clientCompositionDisplay; +} + +std::vector<compositionengine::LayerFE::LayerSettings> +ScreenCaptureOutput::generateClientCompositionRequests( + bool supportsProtectedContent, ui::Dataspace outputDataspace, + std::vector<compositionengine::LayerFE*>& outLayerFEs) { + auto clientCompositionLayers = compositionengine::impl::Output:: + generateClientCompositionRequests(supportsProtectedContent, outputDataspace, + outLayerFEs); + + if (mRegionSampling) { + for (auto& layer : clientCompositionLayers) { + layer.backgroundBlurRadius = 0; + layer.blurRegions.clear(); + } + } + + Rect sourceCrop = mRenderArea.getSourceCrop(); + compositionengine::LayerFE::LayerSettings fillLayer; + fillLayer.source.buffer.buffer = nullptr; + fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f); + fillLayer.geometry.boundaries = + FloatRect(static_cast<float>(sourceCrop.left), static_cast<float>(sourceCrop.top), + static_cast<float>(sourceCrop.right), static_cast<float>(sourceCrop.bottom)); + fillLayer.alpha = half(RenderArea::getCaptureFillValue(mRenderArea.getCaptureFill())); + clientCompositionLayers.insert(clientCompositionLayers.begin(), fillLayer); + + return clientCompositionLayers; +} + +bool ScreenCaptureOutput::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const { + return mRenderArea.needsFiltering() || + mFilterForScreenshot.find(&layer->getLayerFE()) != mFilterForScreenshot.end(); +} + +} // namespace android diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h new file mode 100644 index 0000000000..5dffc1d530 --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -0,0 +1,72 @@ +/* + * Copyright 2022 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 <compositionengine/DisplayColorProfile.h> +#include <compositionengine/RenderSurface.h> +#include <compositionengine/impl/Output.h> +#include <ui/Rect.h> + +#include "RenderArea.h" + +namespace android { + +struct ScreenCaptureOutputArgs { + const compositionengine::CompositionEngine& compositionEngine; + const compositionengine::Output::ColorProfile& colorProfile; + const RenderArea& renderArea; + ui::LayerStack layerStack; + std::shared_ptr<renderengine::ExternalTexture> buffer; + float sdrWhitePointNits; + float displayBrightnessNits; + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot; + bool regionSampling; +}; + +// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer. +// +// SurfaceFlinger passes instances of ScreenCaptureOutput to CompositionEngine in calls to +// SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay. +class ScreenCaptureOutput : public compositionengine::impl::Output { +public: + ScreenCaptureOutput(const RenderArea& renderArea, + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot, + const compositionengine::Output::ColorProfile& colorProfile, + bool regionSampling); + + void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; + + std::vector<compositionengine::LayerFE::LayerSettings> generateClientCompositionRequests( + bool supportsProtectedContent, ui::Dataspace outputDataspace, + std::vector<compositionengine::LayerFE*>& outLayerFEs) override; + + bool layerNeedsFiltering(const compositionengine::OutputLayer*) const override; + +protected: + bool getSkipColorTransform() const override { return false; } + renderengine::DisplaySettings generateClientCompositionDisplaySettings() const override; + +private: + const RenderArea& mRenderArea; + const std::unordered_set<compositionengine::LayerFE*> mFilterForScreenshot; + const compositionengine::Output::ColorProfile& mColorProfile; + const bool mRegionSampling; +}; + +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs); + +} // namespace android diff --git a/services/surfaceflinger/ScreenCaptureRenderSurface.h b/services/surfaceflinger/ScreenCaptureRenderSurface.h new file mode 100644 index 0000000000..20973003d5 --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureRenderSurface.h @@ -0,0 +1,81 @@ +/* + * Copyright 2022 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 <memory> + +#include <compositionengine/RenderSurface.h> +#include <renderengine/impl/ExternalTexture.h> +#include <ui/Fence.h> +#include <ui/Size.h> + +namespace android { + +// ScreenCaptureRenderSurface is a RenderSurface that returns a preallocated buffer used by +// ScreenCaptureOutput. +class ScreenCaptureRenderSurface : public compositionengine::RenderSurface { +public: + ScreenCaptureRenderSurface(std::shared_ptr<renderengine::ExternalTexture> buffer) + : mBuffer(std::move(buffer)){}; + + std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer( + base::unique_fd* /* bufferFence */) override { + return mBuffer; + } + + void queueBuffer(base::unique_fd readyFence) override { + mRenderFence = sp<Fence>::make(readyFence.release()); + } + + const sp<Fence>& getClientTargetAcquireFence() const override { return mRenderFence; } + + bool supportsCompositionStrategyPrediction() const override { return false; } + + bool isValid() const override { return true; } + + void initialize() override {} + + const ui::Size& getSize() const override { return mSize; } + + bool isProtected() const override { return mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED; } + + void setDisplaySize(const ui::Size&) override {} + + void setBufferDataspace(ui::Dataspace) override {} + + void setBufferPixelFormat(ui::PixelFormat) override {} + + void setProtected(bool /* useProtected */) override {} + + status_t beginFrame(bool /* mustRecompose */) override { return OK; } + + void prepareFrame(bool /* usesClientComposition */, bool /* usesDeviceComposition */) override { + } + + void onPresentDisplayCompleted() override {} + + void dump(std::string& /* result */) const override {} + +private: + std::shared_ptr<renderengine::ExternalTexture> mBuffer; + + sp<Fence> mRenderFence = Fence::NO_FENCE; + + ui::Size mSize; +}; + +} // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 79ed7eb292..cdd604495e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -30,6 +30,7 @@ #include <android-base/strings.h> #include <android/configuration.h> #include <android/gui/IDisplayEventConnection.h> +#include <android/gui/StaticDisplayInfo.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> @@ -43,18 +44,23 @@ #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> +#include <compositionengine/DisplayColorProfileCreationArgs.h> #include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/RenderSurface.h> +#include <compositionengine/impl/DisplayColorProfile.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <configstore/Utils.h> #include <cutils/compiler.h> #include <cutils/properties.h> +#include <ftl/algorithm.h> +#include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> -#include <ftl/small_map.h> +#include <ftl/unit.h> +#include <gui/AidlStatusUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> @@ -81,6 +87,7 @@ #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> #include <ui/GraphicBufferAllocator.h> +#include <ui/LayerStack.h> #include <ui/PixelFormat.h> #include <ui/StaticDisplayInfo.h> #include <utils/StopWatch.h> @@ -100,15 +107,14 @@ #include <optional> #include <type_traits> #include <unordered_map> +#include <vector> #include <ui/DisplayIdentification.h> #include "BackgroundExecutor.h" -#include "BufferLayer.h" -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" #include "Client.h" +#include "ClientCache.h" #include "Colorizer.h" -#include "ContainerLayer.h" +#include "Display/DisplayMap.h" #include "DisplayDevice.h" #include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/FramebufferSurface.h" @@ -117,33 +123,31 @@ #include "DisplayHardware/PowerAdvisor.h" #include "DisplayHardware/VirtualDisplaySurface.h" #include "DisplayRenderArea.h" -#include "EffectLayer.h" #include "Effects/Daltonizer.h" #include "FlagManager.h" #include "FpsReporter.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHandle.h" #include "HdrLayerInfoReporter.h" #include "Layer.h" #include "LayerProtoHelper.h" #include "LayerRenderArea.h" #include "LayerVector.h" -#include "MonitoredProducer.h" #include "MutexUtils.h" #include "NativeWindowSurface.h" -#include "RefreshRateOverlay.h" #include "RegionSamplingThread.h" -#include "Scheduler/DispSyncSource.h" #include "Scheduler/EventThread.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncConfiguration.h" -#include "Scheduler/VsyncController.h" +#include "ScreenCaptureOutput.h" #include "StartPropertySetThread.h" #include "SurfaceFlingerProperties.h" -#include "SurfaceInterceptor.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" +#include "Utils/Dumper.h" #include "WindowInfosListenerInvoker.h" #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> @@ -154,9 +158,15 @@ #define NO_THREAD_SAFETY_ANALYSIS \ _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"") +// To enable layer borders in the system, change the below flag to true. +#undef DOES_CONTAIN_BORDER +#define DOES_CONTAIN_BORDER false + namespace android { +using namespace std::chrono_literals; using namespace std::string_literals; +using namespace std::string_view_literals; using namespace hardware::configstore; using namespace hardware::configstore::V1_0; @@ -169,47 +179,28 @@ using CompositionStrategyPredictionState = android::compositionengine::impl:: OutputCompositionState::CompositionStrategyPredictionState; using base::StringAppendF; +using display::PhysicalDisplay; +using display::PhysicalDisplays; +using frontend::TransactionHandler; using gui::DisplayInfo; +using gui::GameMode; using gui::IDisplayEventConnection; using gui::IWindowInfosListener; +using gui::LayerMetadata; using gui::WindowInfo; -using ui::ColorMode; +using gui::aidl_utils::binderStatusFromStatusT; using ui::Dataspace; using ui::DisplayPrimaries; using ui::RenderIntent; -using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController; +using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; namespace hal = android::hardware::graphics::composer::hal; namespace { -#pragma clang diagnostic push -#pragma clang diagnostic error "-Wswitch-enum" - -bool isWideColorMode(const ColorMode colorMode) { - switch (colorMode) { - case ColorMode::DISPLAY_P3: - case ColorMode::ADOBE_RGB: - case ColorMode::DCI_P3: - case ColorMode::BT2020: - case ColorMode::DISPLAY_BT2020: - case ColorMode::BT2100_PQ: - case ColorMode::BT2100_HLG: - return true; - 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; -} - -#pragma clang diagnostic pop +static constexpr int FOUR_K_WIDTH = 3840; +static constexpr int FOUR_K_HEIGHT = 2160; // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity. constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV; @@ -258,6 +249,44 @@ bool getKernelIdleTimerSyspropConfig(DisplayId displayId) { return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false); } +bool isAbove4k30(const ui::DisplayMode& outMode) { + using fps_approx_ops::operator>; + Fps refreshRate = Fps::fromValue(outMode.refreshRate); + return outMode.resolution.getWidth() >= FOUR_K_WIDTH && + outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz; +} + +void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes, + ui::DisplayMode& outMode) { + if (isAbove4k30(outMode) && + std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) { + for (ui::Hdr type : displayHdrTypes) { + if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) { + outMode.supportedHdrTypes.push_back(type); + } + } + } else { + for (ui::Hdr type : displayHdrTypes) { + if (type != ui::Hdr::DOLBY_VISION_4K30) { + outMode.supportedHdrTypes.push_back(type); + } + } + } +} + +HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) { + std::vector<ui::Hdr> hdrTypes; + for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) { + if (type != ui::Hdr::DOLBY_VISION_4K30) { + hdrTypes.push_back(type); + } + } + return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(), + displayHdrCapabilities.getDesiredMaxAverageLuminance(), + displayHdrCapabilities.getDesiredMinLuminance()}; +} + } // namespace anonymous // --------------------------------------------------------------------------- @@ -273,6 +302,8 @@ const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW" const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled"; +static const int MAX_TRACING_MEMORY = 1024 * 1024 * 1024; // 1GB + // --------------------------------------------------------------------------- int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; @@ -280,7 +311,6 @@ bool SurfaceFlinger::hasSyncFramework; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; uint32_t SurfaceFlinger::maxGraphicsWidth; uint32_t SurfaceFlinger::maxGraphicsHeight; -bool SurfaceFlinger::hasWideColorDisplay; bool SurfaceFlinger::useContextPriority; Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB; ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888; @@ -321,18 +351,17 @@ bool callingThreadHasInternalSystemWindowAccess() { SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) : mFactory(factory), mPid(getpid()), - mInterceptor(mFactory.createSurfaceInterceptor()), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(mFactory.createFrameTracer()), mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)), mCompositionEngine(mFactory.createCompositionEngine()), mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)), - mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()), + mTunnelModeEnabledReporter(sp<TunnelModeEnabledReporter>::make()), mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), mInternalDisplayDensity( getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)), mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)), - mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) { + mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()) { ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); } @@ -350,11 +379,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI maxGraphicsWidth = std::max(max_graphics_width(0), 0); maxGraphicsHeight = std::max(max_graphics_height(0), 0); - hasWideColorDisplay = has_wide_color_display(false); + mSupportsWideColor = has_wide_color_display(false); mDefaultCompositionDataspace = static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB)); mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace( - hasWideColorDisplay ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB)); + mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB)); defaultCompositionDataspace = mDefaultCompositionDataspace; wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace; defaultCompositionPixelFormat = static_cast<ui::PixelFormat>( @@ -378,9 +407,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI // debugging stuff... char value[PROPERTY_VALUE_MAX]; - property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); - mGpuToCpuSupported = !atoi(value); - property_get("ro.build.type", value, "user"); mIsUserBuild = strcmp(value, "user") == 0; @@ -391,7 +417,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI int debugDdms = atoi(value); ALOGI_IF(debugDdms, "DDMS debugging not supported"); - property_get("debug.sf.enable_gl_backpressure", value, "0"); + property_get("debug.sf.enable_gl_backpressure", value, "1"); mPropagateBackpressureClientComposition = atoi(value); ALOGI_IF(mPropagateBackpressureClientComposition, "Enabling backpressure propagation for Client Composition"); @@ -403,7 +429,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI property_get("ro.sf.blurs_are_expensive", value, "0"); mBlursAreExpensive = atoi(value); - const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS; + const size_t defaultListSize = MAX_LAYERS; auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize)); mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize; mGraphicBufferProducerListSizeLogThreshold = @@ -438,7 +464,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI android::hardware::details::setTrebleTestingOverride(true); } - mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0); + mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0); + mRefreshRateOverlayRenderRate = + property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0); if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) { mTransactionTracing.emplace(); @@ -453,14 +481,14 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { - if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) { - return LatchUnsignaledConfig::Always; - } - if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) { return LatchUnsignaledConfig::AutoSingleLayer; } + if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) { + return LatchUnsignaledConfig::Always; + } + return LatchUnsignaledConfig::Disabled; } @@ -471,7 +499,8 @@ void SurfaceFlinger::binderDied(const wp<IBinder>&) { mBootFinished = false; // Sever the link to inputflinger since it's gone as well. - static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; })); + static_cast<void>(mScheduler->schedule( + [this] { mInputFlinger.clear(); })); // restore initial conditions (default device unblank, etc) initializeDisplays(); @@ -484,11 +513,6 @@ void SurfaceFlinger::run() { mScheduler->run(); } -sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { - const sp<Client> client = new Client(this); - return client->initCheck() == NO_ERROR ? client : nullptr; -} - sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) { // onTransact already checks for some permissions, but adding an additional check here. // This is to ensure that only system and graphics can request to create a secure @@ -504,7 +528,7 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secur virtual ~DisplayToken() { // no more references, this display must be terminated Mutex::Autolock _l(flinger->mStateLock); - flinger->mCurrentState.displays.removeItem(this); + flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); flinger->setTransactionFlags(eDisplayTransactionNeeded); } public: @@ -513,7 +537,7 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secur } }; - sp<BBinder> token = new DisplayToken(this); + sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this)); Mutex::Autolock _l(mStateLock); // Display ID is assigned when virtual display is allocated by HWC. @@ -521,7 +545,6 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secur state.isSecure = secure; state.displayName = displayName; mCurrentState.displays.add(token, state); - mInterceptor->saveDisplayCreation(state); return token; } @@ -539,7 +562,6 @@ void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) { ALOGE("%s: Invalid operation on physical display", __func__); return; } - mInterceptor->saveDisplayDeletion(state.sequenceId); mCurrentState.displays.removeItemsAt(index); setTransactionFlags(eDisplayTransactionNeeded); } @@ -591,12 +613,12 @@ void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) { std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const { std::vector<PhysicalDisplayId> displayIds; - displayIds.reserve(mPhysicalDisplayTokens.size()); + displayIds.reserve(mPhysicalDisplays.size()); const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId(); displayIds.push_back(defaultDisplayId); - for (const auto& [id, token] : mPhysicalDisplayTokens) { + for (const auto& [id, display] : mPhysicalDisplays) { if (id != defaultDisplayId) { displayIds.push_back(id); } @@ -605,10 +627,10 @@ std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() con return displayIds; } -status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const { - Mutex::Autolock lock(mStateLock); - *id = getPrimaryDisplayIdLocked(); - return NO_ERROR; +std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked( + const sp<display::DisplayToken>& displayToken) const { + return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_key<PhysicalDisplays>); } sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const { @@ -629,7 +651,7 @@ HWComposer& SurfaceFlinger::getHwComposer() const { } renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const { - return mCompositionEngine->getRenderEngine(); + return *mRenderEngine; } compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const { @@ -661,7 +683,7 @@ void SurfaceFlinger::bootFinished() { const String16 name("window"); mWindowManager = defaultServiceManager()->getService(name); if (mWindowManager != 0) { - mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this)); } // stop boot animation @@ -675,7 +697,7 @@ void SurfaceFlinger::bootFinished() { sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger"))); - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { @@ -703,8 +725,9 @@ void SurfaceFlinger::bootFinished() { mBootStage = BootStage::FINISHED; - if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) { - FTL_FAKE_GUARD(mStateLock, enableRefreshRateOverlay(true)); + if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) { + ftl::FakeGuard guard(mStateLock); + enableRefreshRateOverlay(true); } })); } @@ -758,6 +781,10 @@ chooseRenderEngineTypeViaSysProp() { return renderengine::RenderEngine::RenderEngineType::SKIA_GL; } else if (strcmp(prop, "skiaglthreaded") == 0) { return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; + } else if (strcmp(prop, "skiavk") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_VK; + } else if (strcmp(prop, "skiavkthreaded") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED; } else { ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop); return {}; @@ -766,10 +793,11 @@ chooseRenderEngineTypeViaSysProp() { // Do not call property_set on main thread which will be blocked by init // Use StartPropertySetThread instead. -void SurfaceFlinger::init() { +void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - Mutex::Autolock _l(mStateLock); + addTransactionReadyFilters(); + Mutex::Autolock lock(mStateLock); // Get a RenderEngine for the given display / config (can't fail) // TODO(b/77156734): We need to stop casting and use HAL types when possible. @@ -788,7 +816,8 @@ void SurfaceFlinger::init() { if (auto type = chooseRenderEngineTypeViaSysProp()) { builder.setRenderEngineType(type.value()); } - mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build())); + mRenderEngine = renderengine::RenderEngine::create(builder.build()); + mCompositionEngine->setRenderEngine(mRenderEngine.get()); mMaxRenderTargetSize = std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims()); @@ -808,13 +837,36 @@ void SurfaceFlinger::init() { enableHalVirtualDisplays(true); } - // Process any initial hotplug and resulting display changes. - processDisplayHotplugEventsLocked(); - const auto display = getDefaultDisplayDeviceLocked(); - LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback."); - const auto displayId = display->getPhysicalId(); - LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId), - "Primary display is disconnected."); + // Process hotplug for displays connected at boot. + LOG_ALWAYS_FATAL_IF(!configureLocked(), + "Initial display configuration failed: HWC did not hotplug"); + + // Commit primary display. + sp<const DisplayDevice> display; + if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) { + const auto& displays = mCurrentState.displays; + + const auto& token = displays.keyAt(*indexOpt); + const auto& state = displays.valueAt(*indexOpt); + + processDisplayAdded(token, state); + mDrawingState.displays.add(token, state); + + display = getDefaultDisplayDeviceLocked(); + } + + LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display"); + LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()), + "Primary display is disconnected"); + + // TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of + // the DisplayDevice, hence the above commit of the primary display. Remove that special case by + // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice. + initScheduler(display); + dispatchDisplayHotplugEvent(display->getPhysicalId(), true); + + // Commit secondary display(s). + processDisplayChangesLocked(); // initialize our drawing state mDrawingState = mCurrentState; @@ -838,8 +890,6 @@ void SurfaceFlinger::init() { } } - onActiveDisplaySizeChanged(display); - // Inform native graphics APIs whether the present timestamp is supported: const bool presentFenceReliable = @@ -866,8 +916,8 @@ void SurfaceFlinger::readPersistentProperties() { property_get("persist.sys.sf.native_mode", value, "0"); mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value)); - property_get("persist.sys.sf.color_mode", value, "0"); - mForceColorMode = static_cast<ColorMode>(atoi(value)); + mForceColorMode = + static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0)); } void SurfaceFlinger::startBootAnim() { @@ -882,17 +932,6 @@ void SurfaceFlinger::startBootAnim() { // ---------------------------------------------------------------------------- -bool SurfaceFlinger::authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& bufferProducer) const { - Mutex::Autolock _l(mStateLock); - return authenticateSurfaceTextureLocked(bufferProducer); -} - -bool SurfaceFlinger::authenticateSurfaceTextureLocked( - const sp<IGraphicBufferProducer>& /* bufferProducer */) const { - return false; -} - status_t SurfaceFlinger::getSupportedFrameTimestamps( std::vector<FrameEvent>* outSupported) const { *outSupported = { @@ -936,24 +975,24 @@ status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::Di return NO_ERROR; } -status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken, - ui::StaticDisplayInfo* info) { - if (!displayToken || !info) { +status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) { + if (!info) { return BAD_VALUE; } Mutex::Autolock lock(mStateLock); + const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId)); + const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot()); - const auto display = getDisplayDeviceLocked(displayToken); - if (!display) { + if (!displayOpt) { return NAME_NOT_FOUND; } - if (const auto connectionType = display->getConnectionType()) - info->connectionType = *connectionType; - else { - return INVALID_OPERATION; - } + const auto& [display, snapshotRef] = *displayOpt; + const auto& snapshot = snapshotRef.get(); + + info->connectionType = snapshot.connectionType(); + info->deviceProductInfo = snapshot.deviceProductInfo(); if (mEmulatedDisplayDensity) { info->density = mEmulatedDisplayDensity; @@ -965,37 +1004,19 @@ status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken, info->density /= ACONFIGURATION_DENSITY_MEDIUM; info->secure = display->isSecure(); - info->deviceProductInfo = display->getDeviceProductInfo(); info->installOrientation = display->getPhysicalOrientation(); return NO_ERROR; } -status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, - ui::DynamicDisplayInfo* info) { - if (!displayToken || !info) { - return BAD_VALUE; - } - - Mutex::Autolock lock(mStateLock); - - const auto display = getDisplayDeviceLocked(displayToken); - if (!display) { - return NAME_NOT_FOUND; - } - - const auto displayId = PhysicalDisplayId::tryCast(display->getId()); - if (!displayId) { - return INVALID_OPERATION; - } - - info->activeDisplayModeId = display->getActiveMode()->getId().value(); - - const auto& supportedModes = display->getSupportedModes(); +void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info, + const sp<DisplayDevice>& display, + const display::DisplaySnapshot& snapshot) { + const auto& displayModes = snapshot.displayModes(); info->supportedDisplayModes.clear(); - info->supportedDisplayModes.reserve(supportedModes.size()); + info->supportedDisplayModes.reserve(displayModes.size()); - for (const auto& [id, mode] : supportedModes) { + for (const auto& [id, mode] : displayModes) { ui::DisplayMode outMode; outMode.id = static_cast<int32_t>(id.value()); @@ -1035,106 +1056,175 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, // We add an additional 1ms to allow for processing time and // differences between the ideal and actual refresh rate. outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000; - + excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(), + outMode); info->supportedDisplayModes.push_back(outMode); } + info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor); + + const PhysicalDisplayId displayId = snapshot.displayId(); + + const auto mode = display->refreshRateSelector().getActiveMode(); + info->activeDisplayModeId = mode.modePtr->getId().value(); + info->renderFrameRate = mode.fps.getValue(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; - info->supportedColorModes = getDisplayColorModes(*display); - info->hdrCapabilities = display->getHdrCapabilities(); + info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); info->autoLowLatencyModeSupported = - getHwComposer().hasDisplayCapability(*displayId, + getHwComposer().hasDisplayCapability(displayId, DisplayCapability::AUTO_LOW_LATENCY_MODE); info->gameContentTypeSupported = - getHwComposer().supportsContentType(*displayId, hal::ContentType::GAME); + getHwComposer().supportsContentType(displayId, hal::ContentType::GAME); info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1); if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) { - if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(*displayId)) { - if (const auto modeId = display->translateModeId(*hwcId)) { + if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) { + if (const auto modeId = snapshot.translateModeId(*hwcId)) { info->preferredBootDisplayMode = modeId->value(); } } } +} + +status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId, + ui::DynamicDisplayInfo* info) { + if (!info) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mStateLock); + + const auto id_ = + DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId)); + const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot()); + + if (!displayOpt) { + return NAME_NOT_FOUND; + } + const auto& [display, snapshotRef] = *displayOpt; + getDynamicDisplayInfoInternal(info, display, snapshotRef.get()); return NO_ERROR; } -status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) { - if (!stats) { +status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken, + ui::DynamicDisplayInfo* info) { + if (!displayToken || !info) { return BAD_VALUE; } - *stats = mScheduler->getDisplayStatInfo(systemTime()); + Mutex::Autolock lock(mStateLock); + + const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_mapped_ref<PhysicalDisplays>) + .and_then(getDisplayDeviceAndSnapshot()); + + if (!displayOpt) { + return NAME_NOT_FOUND; + } + + const auto& [display, snapshotRef] = *displayOpt; + getDynamicDisplayInfoInternal(info, display, snapshotRef.get()); + return NO_ERROR; +} + +status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) { + if (!outStats) { + return BAD_VALUE; + } + + const auto& schedule = mScheduler->getVsyncSchedule(); + outStats->vsyncTime = schedule.vsyncDeadlineAfter(TimePoint::now()).ns(); + outStats->vsyncPeriod = schedule.period().ns(); return NO_ERROR; } -void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { +void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) { ATRACE_CALL(); - if (!info.mode) { - ALOGW("requested display mode is null"); - return; - } - auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId()); + auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId()); if (!display) { ALOGW("%s: display is no longer valid", __func__); return; } - if (display->setDesiredActiveMode(info)) { - scheduleComposite(FrameHint::kNone); + const auto mode = request.mode; + const bool emitEvent = request.emitEvent; + + switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) { + case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch: + scheduleComposite(FrameHint::kNone); - // Start receiving vsync samples now, so that we can detect a period - // switch. - mScheduler->resyncToHardwareVsync(true, info.mode->getFps()); - // As we called to set period, we will call to onRefreshRateChangeCompleted once - // VsyncController model is locked. - modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); + // Start receiving vsync samples now, so that we can detect a period + // switch. + mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps()); + // As we called to set period, we will call to onRefreshRateChangeCompleted once + // VsyncController model is locked. + modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); - updatePhaseConfiguration(info.mode->getFps()); - mScheduler->setModeChangePending(true); + updatePhaseConfiguration(mode.fps); + mScheduler->setModeChangePending(true); + break; + case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch: + mScheduler->setRenderRate(mode.fps); + updatePhaseConfiguration(mode.fps); + mRefreshRateStats->setRefreshRate(mode.fps); + if (display->getPhysicalId() == mActiveDisplayId && emitEvent) { + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode); + } + + break; + case DisplayDevice::DesiredActiveModeAction::None: + break; } } -status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) { +status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken, + DisplayModeId modeId) { ATRACE_CALL(); if (!displayToken) { return BAD_VALUE; } - auto future = mScheduler->schedule([=]() -> status_t { - const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); - if (!display) { - ALOGE("Attempt to set allowed display modes for invalid display token %p", - displayToken.get()); + const char* const whence = __func__; + auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + const auto displayOpt = + FTL_FAKE_GUARD(mStateLock, + ftl::find_if(mPhysicalDisplays, + PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_mapped_ref<PhysicalDisplays>) + .and_then(getDisplayDeviceAndSnapshot())); + if (!displayOpt) { + ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); return NAME_NOT_FOUND; } - if (display->isVirtual()) { - ALOGW("Attempt to set allowed display modes for virtual display"); - return INVALID_OPERATION; - } + const auto& [display, snapshotRef] = *displayOpt; + const auto& snapshot = snapshotRef.get(); + + const auto fpsOpt = snapshot.displayModes().get(modeId).transform( + [](const DisplayModePtr& mode) { return mode->getFps(); }); - const auto mode = display->getMode(DisplayModeId{modeId}); - if (!mode) { - ALOGW("Attempt to switch to an unsupported mode %d.", modeId); + if (!fpsOpt) { + ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } - const auto fps = mode->getFps(); + const Fps fps = *fpsOpt; + // Keep the old switching type. - const auto allowGroupSwitching = - display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching; - const scheduler::RefreshRateConfigs::Policy policy{mode->getId(), - allowGroupSwitching, - {fps, fps}}; - constexpr bool kOverridePolicy = false; - - return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy); + const bool allowGroupSwitching = + display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching; + + const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, + {fps, fps}, + allowGroupSwitching}; + + return setDesiredDisplayModeSpecsInternal(display, policy); }); return future.get(); @@ -1148,52 +1238,58 @@ void SurfaceFlinger::updateInternalStateWithChangedMode() { return; } - const auto upcomingModeInfo = - FTL_FAKE_GUARD(kMainThreadContext, display->getUpcomingActiveMode()); - - if (!upcomingModeInfo.mode) { + const auto upcomingModeInfo = display->getUpcomingActiveMode(); + if (!upcomingModeInfo.modeOpt) { // There is no pending mode change. This can happen if the active // display changed and the mode change happened on a different display. return; } - if (display->getActiveMode()->getResolution() != upcomingModeInfo.mode->getResolution()) { + if (display->getActiveMode().modePtr->getResolution() != + upcomingModeInfo.modeOpt->modePtr->getResolution()) { auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken()); // We need to generate new sequenceId in order to recreate the display (and this // way the framebuffer). state.sequenceId = DisplayDeviceState{}.sequenceId; - state.physical->activeMode = upcomingModeInfo.mode; + state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get(); processDisplayChangesLocked(); // processDisplayChangesLocked will update all necessary components so we're done here. return; } - // We just created this display so we can call even if we are not on the main thread. - ftl::FakeGuard guard(kMainThreadContext); - display->setActiveMode(upcomingModeInfo.mode->getId()); + mPhysicalDisplays.get(display->getPhysicalId()) + .transform(&PhysicalDisplay::snapshotRef) + .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { + FTL_FAKE_GUARD(kMainThreadContext, + display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(), + upcomingModeInfo.modeOpt->modePtr->getFps(), + upcomingModeInfo.modeOpt->fps)); + })); - const Fps refreshRate = upcomingModeInfo.mode->getFps(); + const Fps refreshRate = upcomingModeInfo.modeOpt->fps; mRefreshRateStats->setRefreshRate(refreshRate); updatePhaseConfiguration(refreshRate); - if (upcomingModeInfo.event != DisplayModeEvent::None) { - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode); + if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) { + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt); } } void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) { display->clearDesiredActiveModeState(); - if (isDisplayActiveLocked(display)) { + if (display->getPhysicalId() == mActiveDisplayId) { mScheduler->setModeChangePending(false); } } void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) { - const auto refreshRate = display->getDesiredActiveMode()->mode->getFps(); + const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps(); + const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps; clearDesiredActiveModeState(display); - mScheduler->resyncToHardwareVsync(true, refreshRate); - updatePhaseConfiguration(refreshRate); + mScheduler->resyncToHardwareVsync(true, displayFps); + mScheduler->setRenderRate(renderFps); + updatePhaseConfiguration(renderFps); } void SurfaceFlinger::setActiveModeInHwcIfNeeded() { @@ -1201,39 +1297,44 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { std::optional<PhysicalDisplayId> displayToUpdateImmediately; - for (const auto& iter : mDisplays) { - const auto& display = iter.second; - if (!display || !display->isInternal()) { + for (const auto& [id, physical] : mPhysicalDisplays) { + const auto& snapshot = physical.snapshot(); + + if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) { continue; } + const auto display = getDisplayDeviceLocked(id); + if (!display) continue; + // Store the local variable to release the lock. const auto desiredActiveMode = display->getDesiredActiveMode(); if (!desiredActiveMode) { - // No desired active mode pending to be applied + // No desired active mode pending to be applied. continue; } - if (!isDisplayActiveLocked(display)) { - // display is no longer the active display, so abort the mode change + if (id != mActiveDisplayId) { + // Display is no longer the active display, so abort the mode change. clearDesiredActiveModeState(display); continue; } - const auto desiredMode = display->getMode(desiredActiveMode->mode->getId()); - if (!desiredMode) { + const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId(); + const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId); + + if (!displayModePtrOpt) { ALOGW("Desired display mode is no longer supported. Mode ID = %d", - desiredActiveMode->mode->getId().value()); + desiredModeId.value()); clearDesiredActiveModeState(display); continue; } - const auto refreshRate = desiredMode->getFps(); - ALOGV("%s changing active mode to %d(%s) for display %s", __func__, - desiredMode->getId().value(), to_string(refreshRate).c_str(), + ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(), + to_string(displayModePtrOpt->get()->getFps()).c_str(), to_string(display->getId()).c_str()); - if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) { + if (display->getActiveMode() == desiredActiveMode->modeOpt) { // we are already in the requested mode, there is nothing left to do desiredActiveModeChangeDone(display); continue; @@ -1243,7 +1344,7 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { // allowed modes might have changed by the time we process the refresh. // Make sure the desired mode is still allowed const auto displayModeAllowed = - display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId()); + display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt); if (!displayModeAllowed) { clearDesiredActiveModeState(display); continue; @@ -1255,9 +1356,8 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { constraints.seamlessRequired = false; hal::VsyncPeriodChangeTimeline outTimeline; - const auto status = FTL_FAKE_GUARD(kMainThreadContext, - display->initiateModeChange(*desiredActiveMode, - constraints, &outTimeline)); + const auto status = + display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline); if (status != NO_ERROR) { // initiateModeChange may fail if a hotplug event is just about @@ -1265,6 +1365,8 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { ALOGW("initiateModeChange failed: %d", status); continue; } + + display->refreshRateSelector().onModeChangeInitiated(); mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); if (outTimeline.refreshRequired) { @@ -1283,8 +1385,7 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately); const auto desiredActiveMode = display->getDesiredActiveMode(); - if (desiredActiveMode && - display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) { + if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) { desiredActiveModeChangeDone(display); } } @@ -1305,22 +1406,6 @@ void SurfaceFlinger::disableExpensiveRendering() { future.wait(); } -std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) { - auto modes = getHwComposer().getColorModes(display.getPhysicalId()); - - // If the display is internal and the configuration claims it's not wide color capable, - // filter out all wide color modes. The typical reason why this happens is that the - // hardware is not good enough to support GPU composition of wide color, and thus the - // OEMs choose to disable this capability. - if (display.getConnectionType() == ui::DisplayConnectionType::Internal && - !hasWideColorDisplay) { - const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode); - modes.erase(newEnd, modes.end()); - } - - return modes; -} - status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries& primaries) { if (!displayToken) { @@ -1329,13 +1414,13 @@ status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayTok Mutex::Autolock lock(mStateLock); - const auto display = getDisplayDeviceLocked(displayToken); + const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_mapped_ref<PhysicalDisplays>); if (!display) { return NAME_NOT_FOUND; } - const auto connectionType = display->getConnectionType(); - if (connectionType != ui::DisplayConnectionType::Internal) { + if (!display.transform(&PhysicalDisplay::isInternal).value()) { return INVALID_OPERATION; } @@ -1344,31 +1429,32 @@ status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayTok return NO_ERROR; } -status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) { +status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) { if (!displayToken) { return BAD_VALUE; } + const char* const whence = __func__; auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { - const auto display = getDisplayDeviceLocked(displayToken); - if (!display) { - ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p", - decodeColorMode(mode).c_str(), mode, displayToken.get()); + const auto displayOpt = + ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_mapped_ref<PhysicalDisplays>) + .and_then(getDisplayDeviceAndSnapshot()); + + if (!displayOpt) { + ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); return NAME_NOT_FOUND; } - if (display->isVirtual()) { - ALOGW("Attempt to set active color mode %s (%d) for virtual display", - decodeColorMode(mode).c_str(), mode); - return INVALID_OPERATION; - } + const auto& [display, snapshotRef] = *displayOpt; + const auto& snapshot = snapshotRef.get(); - const auto modes = getDisplayColorModes(*display); + const auto modes = snapshot.filterColorModes(mSupportsWideColor); const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end(); - if (mode < ColorMode::NATIVE || !exists) { - ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p", - decodeColorMode(mode).c_str(), mode, displayToken.get()); + if (mode < ui::ColorMode::NATIVE || !exists) { + ALOGE("%s: Invalid color mode %s (%d) for display %s", whence, + decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } @@ -1391,30 +1477,54 @@ status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const { return NO_ERROR; } -status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, - ui::DisplayModeId modeId) { +status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const { + const auto& aidlProperties = getHwComposer().getOverlaySupport(); + // convert aidl OverlayProperties to gui::OverlayProperties + outProperties->combinations.reserve(aidlProperties.combinations.size()); + for (const auto& combination : aidlProperties.combinations) { + std::vector<int32_t> pixelFormats; + pixelFormats.reserve(combination.pixelFormats.size()); + std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(), + std::back_inserter(pixelFormats), + [](const auto& val) { return static_cast<int32_t>(val); }); + std::vector<int32_t> dataspaces; + dataspaces.reserve(combination.dataspaces.size()); + std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(), + std::back_inserter(dataspaces), + [](const auto& val) { return static_cast<int32_t>(val); }); + gui::OverlayProperties::SupportedBufferCombinations outCombination; + outCombination.pixelFormats = std::move(pixelFormats); + outCombination.dataspaces = std::move(dataspaces); + outProperties->combinations.emplace_back(outCombination); + } + return NO_ERROR; +} + +status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken, + DisplayModeId modeId) { const char* const whence = __func__; auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { - const auto display = getDisplayDeviceLocked(displayToken); - if (!display) { - ALOGE("%s: Invalid display token %p", whence, displayToken.get()); + const auto snapshotOpt = + ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) + .transform(&ftl::to_mapped_ref<PhysicalDisplays>) + .transform(&PhysicalDisplay::snapshotRef); + + if (!snapshotOpt) { + ALOGE("%s: Invalid physical display token %p", whence, displayToken.get()); return NAME_NOT_FOUND; } - if (display->isVirtual()) { - ALOGE("%s: Invalid operation on virtual display", whence); - return INVALID_OPERATION; - } + const auto& snapshot = snapshotOpt->get(); + const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform( + [](const DisplayModePtr& mode) { return mode->getHwcId(); }); - const auto displayId = display->getPhysicalId(); - const auto mode = display->getMode(DisplayModeId{modeId}); - if (!mode) { - ALOGE("%s: Invalid mode %d for display %s", whence, modeId, - to_string(displayId).c_str()); + if (!hwcIdOpt) { + ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } - return getHwComposer().setBootDisplayMode(displayId, mode->getHwcId()); + return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt); }); return future.get(); } @@ -1455,18 +1565,6 @@ void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on })); } -status_t SurfaceFlinger::clearAnimationFrameStats() { - Mutex::Autolock _l(mStateLock); - mAnimFrameTracker.clearStats(); - return NO_ERROR; -} - -status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { - Mutex::Autolock _l(mStateLock); - mAnimFrameTracker.getStats(outStats); - return NO_ERROR; -} - status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken, const std::vector<ui::Hdr>& hdrTypes) { Mutex::Autolock lock(mStateLock); @@ -1482,7 +1580,8 @@ status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken, return NO_ERROR; } -status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) { +status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, + bool* success) { *success = mTimeStats->onPullAtom(atomId, pulledData); return NO_ERROR; } @@ -1557,34 +1656,11 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, } *outIsWideColorDisplay = - display->isPrimary() ? hasWideColorDisplay : display->hasWideColorGamut(); + display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut(); return NO_ERROR; } -status_t SurfaceFlinger::enableVSyncInjections(bool enable) { - auto future = mScheduler->schedule([=] { - Mutex::Autolock lock(mStateLock); - - if (const auto handle = mScheduler->enableVSyncInjection(enable)) { - mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr); - } - }); - - future.wait(); - return NO_ERROR; -} - -status_t SurfaceFlinger::injectVSync(nsecs_t when) { - Mutex::Autolock lock(mStateLock); - const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when); - const auto expectedPresent = calculateExpectedPresentTime(stats); - return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent, - /*deadlineTimestamp=*/expectedPresent) - ? NO_ERROR - : BAD_VALUE; -} - -status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) { +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) { outLayers->clear(); auto future = mScheduler->schedule([=] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); @@ -1615,8 +1691,12 @@ status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea, return BAD_VALUE; } - const wp<Layer> stopLayer = fromHandle(stopLayerHandle); - mRegionSamplingThread->addListener(samplingArea, stopLayer, listener); + // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy + // the layer here since the caller has a strong ref to the layer's handle. + const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle); + mRegionSamplingThread->addListener(samplingArea, + stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID, + listener); return NO_ERROR; } @@ -1816,10 +1896,11 @@ status_t SurfaceFlinger::getDisplayDecorationSupport( // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::EventRegistrationFlags eventRegistration) { + gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) { const auto& handle = - vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle; + vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger + ? mSfConnectionHandle + : mAppConnectionHandle; return mScheduler->createDisplayEventConnection(handle, eventRegistration); } @@ -1828,7 +1909,7 @@ void SurfaceFlinger::scheduleCommit(FrameHint hint) { if (hint == FrameHint::kActive) { mScheduler->resetIdleTimer(); } - mPowerAdvisor->notifyDisplayUpdateImminent(); + mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset(); mScheduler->scheduleFrame(); } @@ -1867,20 +1948,13 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str()); Mutex::Autolock lock(mStateLock); - const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId); - if (displayId) { - const auto token = getPhysicalDisplayTokenLocked(*displayId); - const auto display = getDisplayDeviceLocked(token); - display->onVsync(timestamp); - } if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) { return; } - const bool isActiveDisplay = - displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken; - if (!isActiveDisplay) { + if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId); + displayId != mActiveDisplayId) { // For now, we don't do anything with non active display vsyncs. return; } @@ -1892,30 +1966,16 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t } } -void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { - std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); - *compositorTiming = getBE().mCompositorTiming; -} - void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { - const bool connected = connection == hal::Connection::CONNECTED; - ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId); - - // Only lock if we're not on the main thread. This function is normally - // called on a hwbinder thread, but for the primary display it's called on - // the main thread with the state lock already held, so don't attempt to - // acquire it here. - ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - - mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection}); - - if (std::this_thread::get_id() == mMainThreadId) { - // Process all pending hot plug events immediately if we are on the main thread. - processDisplayHotplugEventsLocked(); + { + std::lock_guard<std::mutex> lock(mHotplugMutex); + mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection}); } - setTransactionFlags(eDisplayTransactionNeeded); + if (mScheduler) { + mScheduler->scheduleConfigure(); + } } void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged( @@ -1957,26 +2017,15 @@ void SurfaceFlinger::setVsyncEnabled(bool enabled) { })); } -SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() { - const auto now = systemTime(); - const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod; - const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod; - - size_t shift = 0; - if (!expectedPresentTimeIsTheNextVsync) { - shift = static_cast<size_t>((mExpectedPresentTime - now) / vsyncPeriod); - if (shift >= mPreviousPresentFences.size()) { - shift = mPreviousPresentFences.size() - 1; - } - } - ATRACE_FORMAT("previousFrameFence shift=%zu", shift); - return mPreviousPresentFences[shift]; +auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) + -> const FenceTimePtr& { + const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod; + const size_t i = static_cast<size_t>(isTwoVsyncsAhead); + return mPreviousPresentFences[i].fenceTime; } -bool SurfaceFlinger::previousFramePending(int graceTimeMs) { +bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) { ATRACE_CALL(); - const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime; - if (fence == FenceTime::NO_FENCE) { return false; } @@ -1987,44 +2036,45 @@ bool SurfaceFlinger::previousFramePending(int graceTimeMs) { return status == -ETIME; } -nsecs_t SurfaceFlinger::previousFramePresentTime() { - const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime; +TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const { + const auto& schedule = mScheduler->getVsyncSchedule(); - if (fence == FenceTime::NO_FENCE) { - return Fence::SIGNAL_TIME_INVALID; + const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(frameTime); + if (mVsyncModulator->getVsyncConfig().sfOffset > 0) { + return vsyncDeadline; } - return fence->getSignalTime(); + // Inflate the expected present time if we're targeting the next vsync. + return vsyncDeadline + schedule.period(); } -nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const { - // Inflate the expected present time if we're targetting the next vsync. - return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime - : stats.vsyncTime + stats.vsyncPeriod; +void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) { + Mutex::Autolock lock(mStateLock); + if (configureLocked()) { + setTransactionFlags(eDisplayTransactionNeeded); + } } -bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) +bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) FTL_FAKE_GUARD(kMainThreadContext) { - // calculate the expected present time once and use the cached - // value throughout this frame to make sure all layers are - // seeing this same value. - if (expectedVsyncTime >= frameTime) { - mExpectedPresentTime = expectedVsyncTime; - } else { - const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime); - mExpectedPresentTime = calculateExpectedPresentTime(stats); - } - - const nsecs_t lastScheduledPresentTime = mScheduledPresentTime; + // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the + // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime + // accordingly, but not mScheduledPresentTime. + const TimePoint lastScheduledPresentTime = mScheduledPresentTime; mScheduledPresentTime = expectedVsyncTime; - const auto vsyncIn = [&] { - if (!ATRACE_ENABLED()) return 0.f; - return (mExpectedPresentTime - systemTime()) / 1e6f; - }(); - ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn, + // Calculate the expected present time once and use the cached value throughout this frame to + // make sure all layers are seeing this same value. + mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime + : calculateExpectedPresentTime(frameTime); + + ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId.value, + ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)"); + const Period vsyncPeriod = mScheduler->getVsyncSchedule().period(); + const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod); + // When Backpressure propagation is enabled we want to give a small grace period // for the present fence to fire instead of just giving up on this frame to handle cases // where present fence is just about to get signaled. @@ -2033,7 +2083,8 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Pending frames may trigger backpressure propagation. const TracedOrdinal<bool> framePending = {"PrevFramePending", - previousFramePending(graceTimeForPresentFenceMs)}; + isFencePending(previousPresentFence, + graceTimeForPresentFenceMs)}; // Frame missed counts for metrics tracking. // A frame is missed if the prior frame is still pending. If no longer pending, @@ -2043,13 +2094,12 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Add some slop to correct for drift. This should generally be // smaller than a typical frame duration, but should not be so small // that it reports reasonable drift as a missed frame. - const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime()); - const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2; - const nsecs_t previousPresentTime = previousFramePresentTime(); + const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2; + const nsecs_t previousPresentTime = previousPresentFence->getSignalTime(); const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed", framePending || (previousPresentTime >= 0 && - (lastScheduledPresentTime < + (lastScheduledPresentTime.ns() < previousPresentTime - frameMissedSlop))}; const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed", mHadDeviceComposition && frameMissed}; @@ -2069,6 +2119,11 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected mGpuFrameMissedCount++; } + if (mTracingEnabledChanged) { + mLayerTracingEnabled = mLayerTracing.isEnabled(); + mTracingEnabledChanged = false; + } + // If we are in the middle of a mode change and the fence hasn't // fired yet just wait for the next commit. if (mSetActiveModePending) { @@ -2095,20 +2150,20 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Save this once per commit + composite to ensure consistency // TODO (b/240619471): consider removing active display check once AOD is fixed - const auto activeDisplay = - FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken)); + const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId)); mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay && activeDisplay->getPowerMode() == hal::PowerMode::ON; if (mPowerHintSessionEnabled) { const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get(); - // get stable vsync period from display mode - const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod(); + const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs()); mPowerAdvisor->setCommitStart(frameTime); mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); - const nsecs_t idealSfWorkDuration = - mVsyncModulator->getVsyncConfig().sfWorkDuration.count(); - // Frame delay is how long we should have minus how long we actually have - mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime)); + + // Frame delay is how long we should have minus how long we actually have. + const Duration idealSfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration; + const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime); + + mPowerAdvisor->setFrameDelay(frameDelay); mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); @@ -2119,11 +2174,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } } - if (mTracingEnabledChanged) { - mLayerTracingEnabled = mLayerTracing.isEnabled(); - mTracingEnabledChanged = false; - } - if (mRefreshRateOverlaySpinner) { Mutex::Autolock lock(mStateLock); if (const auto display = getDefaultDisplayDeviceLocked()) { @@ -2134,11 +2184,13 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Composite if transactions were committed, or if requested by HWC. bool mustComposite = mMustComposite.exchange(false); { - mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod)); + mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(), + Fps::fromPeriodNsecs(vsyncPeriod.ns())); bool needsTraversal = false; if (clearTransactionFlags(eTransactionFlushNeeded)) { - needsTraversal |= commitCreatedLayers(); + needsTraversal |= commitMirrorDisplays(vsyncId); + needsTraversal |= commitCreatedLayers(vsyncId); needsTraversal |= flushTransactionQueues(vsyncId); } @@ -2173,7 +2225,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer> // and may eventually call to ~Layer() if it holds the last reference { - Mutex::Autolock _l(mStateLock); + Mutex::Autolock lock(mStateLock); mScheduler->chooseRefreshRateForContent(); setActiveModeInHwcIfNeeded(); } @@ -2183,17 +2235,18 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and tracing should only be enabled for debugging. - mLayerTracing.notify(mVisibleRegionsDirty, frameTime); + mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value); } + mLastCommittedVsyncId = vsyncId; persistDisplayBrightness(mustComposite); return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER); } -void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) +void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) FTL_FAKE_GUARD(kMainThreadContext) { - ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); + ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId.value); compositionengine::CompositionRefreshArgs refreshArgs; const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); @@ -2204,10 +2257,31 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) displayIds.push_back(display->getId()); } mPowerAdvisor->setDisplays(displayIds); - mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) { - if (auto layerFE = layer->getCompositionEngineLayerFE()) - refreshArgs.layers.push_back(layerFE); - }); + + const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test( + compositionengine::Feature::kSnapshotLayerMetadata); + if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) { + updateLayerMetadataSnapshot(); + mLayerMetadataSnapshotNeeded = false; + } + + if (DOES_CONTAIN_BORDER) { + refreshArgs.borderInfoList.clear(); + mDrawingState.traverse([&refreshArgs](Layer* layer) { + if (layer->isBorderEnabled()) { + compositionengine::BorderRenderInfo info; + info.width = layer->getBorderWidth(); + info.color = layer->getBorderColor(); + layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) { + info.layerIds.push_back(ilayer->getSequence()); + }); + refreshArgs.borderInfoList.emplace_back(std::move(info)); + } + }); + } + + refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache); + refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); for (auto layer : mLayersWithQueuedFrames) { if (auto layerFE = layer->getCompositionEngineLayerFE()) @@ -2222,6 +2296,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty; + std::vector<Layer*> layers; + + mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) { + if (auto layerFE = layer->getCompositionEngineLayerFE()) { + layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame); + refreshArgs.layers.push_back(layerFE); + layers.push_back(layer); + } + }); refreshArgs.blursAreExpensive = mBlursAreExpensive; refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags(); @@ -2237,26 +2320,45 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const auto expectedPresentTime = mExpectedPresentTime.load(); - const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime); + const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule().period(); const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; + refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration; refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime; refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); - refreshArgs.expectedPresentTime = expectedPresentTime; + refreshArgs.expectedPresentTime = mExpectedPresentTime.ns(); // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); - mCompositionEngine->present(refreshArgs); + { + std::vector<LayerSnapshotGuard> layerSnapshotGuards; + for (Layer* layer : layers) { + layerSnapshotGuards.emplace_back(layer); + } + mCompositionEngine->present(refreshArgs); + } + + for (auto& layer : layers) { + CompositionResult compositionResult{ + layer->getCompositionEngineLayerFE()->stealCompositionResult()}; + layer->onPreComposition(compositionResult.refreshStartTime); + for (auto releaseFence : compositionResult.releaseFences) { + layer->onLayerDisplayed(releaseFence); + } + if (compositionResult.lastClientCompositionFence) { + layer->setWasClientComposed(compositionResult.lastClientCompositionFence); + } + } - mTimeStats->recordFrameDuration(frameTime, systemTime()); + mTimeStats->recordFrameDuration(frameTime.ns(), systemTime()); // Send a power hint hint after presentation is finished if (mPowerHintSessionEnabled) { - mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(), - systemTime()); + mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(mPreviousPresentFences[0] + .fenceTime->getSignalTime()), + TimePoint::now()); if (mPowerHintSessionMode.late) { mPowerAdvisor->sendActualWorkDuration(); } @@ -2266,7 +2368,6 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) scheduleComposite(FrameHint::kNone); } - postFrame(); postComposition(); const bool prevFrameHadClientComposition = mHadClientComposition; @@ -2296,7 +2397,7 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) mLayersWithQueuedFrames.clear(); if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and should only be used for debugging. - mLayerTracing.notify(mVisibleRegionsDirty, frameTime); + mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value); } mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp @@ -2307,7 +2408,7 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) } if (mPowerHintSessionEnabled) { - mPowerAdvisor->setCompositeEnd(systemTime()); + mPowerAdvisor->setCompositeEnd(TimePoint::now()); } } @@ -2326,66 +2427,6 @@ void SurfaceFlinger::updateLayerGeometry() { mLayersPendingRefresh.clear(); } -void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, - std::shared_ptr<FenceTime>& presentFenceTime) { - // Update queue of past composite+present times and determine the - // most recently known composite to present latency. - getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime}); - nsecs_t compositeToPresentLatency = -1; - while (!getBE().mCompositePresentTimes.empty()) { - SurfaceFlingerBE::CompositePresentTime& cpt = getBE().mCompositePresentTimes.front(); - // Cached values should have been updated before calling this method, - // which helps avoid duplicate syscalls. - nsecs_t displayTime = cpt.display->getCachedSignalTime(); - if (displayTime == Fence::SIGNAL_TIME_PENDING) { - break; - } - compositeToPresentLatency = displayTime - cpt.composite; - getBE().mCompositePresentTimes.pop(); - } - - // Don't let mCompositePresentTimes grow unbounded, just in case. - while (getBE().mCompositePresentTimes.size() > 16) { - getBE().mCompositePresentTimes.pop(); - } - - setCompositorTimingSnapped(stats, compositeToPresentLatency); -} - -void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, - nsecs_t compositeToPresentLatency) { - // Avoid division by 0 by defaulting to 60Hz - const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs(); - - // Integer division and modulo round toward 0 not -inf, so we need to - // treat negative and positive offsets differently. - nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0) - ? (vsyncPeriod - - (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % vsyncPeriod)) - : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % vsyncPeriod); - - // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval. - if (idealLatency <= 0) { - idealLatency = vsyncPeriod; - } - - // Snap the latency to a value that removes scheduling jitter from the - // composition and present times, which often have >1ms of jitter. - // Reducing jitter is important if an app attempts to extrapolate - // something (such as user input) to an accurate diasplay time. - // Snapping also allows an app to precisely calculate - // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval). - const nsecs_t bias = vsyncPeriod / 2; - const int64_t extraVsyncs = ((compositeToPresentLatency - idealLatency + bias) / vsyncPeriod); - const nsecs_t snappedCompositeToPresentLatency = - (extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency; - - std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); - getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency; - getBE().mCompositorTiming.interval = vsyncPeriod; - getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; -} - bool SurfaceFlinger::isHdrLayer(Layer* layer) const { // Treat all layers as non-HDR if: // 1. They do not have a valid HDR dataspace. Currently we treat those as PQ or HLG. and @@ -2442,7 +2483,7 @@ ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, void SurfaceFlinger::postComposition() { ATRACE_CALL(); - ALOGV("postComposition"); + ALOGV(__func__); const auto* display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get(); @@ -2456,40 +2497,41 @@ void SurfaceFlinger::postComposition() { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - for (size_t i = mPreviousPresentFences.size()-1; i >= 1; i--) { - mPreviousPresentFences[i] = mPreviousPresentFences[i-1]; - } + mPreviousPresentFences[1] = mPreviousPresentFences[0]; - mPreviousPresentFences[0].fence = + auto presentFence = display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE; - mPreviousPresentFences[0].fenceTime = - std::make_shared<FenceTime>(mPreviousPresentFences[0].fence); - nsecs_t now = systemTime(); + auto presentFenceTime = std::make_shared<FenceTime>(presentFence); + mPreviousPresentFences[0] = {presentFence, presentFenceTime}; + + const TimePoint presentTime = TimePoint::now(); // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. - mFrameTimeline->setSfPresent(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime, - glCompositionDoneFenceTime); - - const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now); + mFrameTimeline->setSfPresent(presentTime.ns(), presentFenceTime, glCompositionDoneFenceTime); // We use the CompositionEngine::getLastFrameRefreshTimestamp() 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, mCompositionEngine->getLastFrameRefreshTimestamp(), - mPreviousPresentFences[0].fenceTime); - CompositorTiming compositorTiming; - { - std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); - compositorTiming = getBE().mCompositorTiming; - } + // but that should be okay since CompositorTiming has snapping logic. + const TimePoint compositeTime = + TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp()); + const Duration presentLatency = + mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime); + + const auto& schedule = mScheduler->getVsyncSchedule(); + const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(presentTime); + const Period vsyncPeriod = schedule.period(); + const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset; + + const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase, + presentLatency.ns()); for (const auto& layer: mLayersWithQueuedFrames) { - layer->onPostComposition(display, glCompositionDoneFenceTime, - mPreviousPresentFences[0].fenceTime, compositorTiming); - layer->releasePendingBuffer(/*dequeueReadyTime*/ now); + layer->onPostComposition(display, glCompositionDoneFenceTime, presentFenceTime, + compositorTiming); + layer->releasePendingBuffer(presentTime.ns()); } std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>> @@ -2546,13 +2588,22 @@ void SurfaceFlinger::postComposition() { mSomeDataspaceChanged = false; mVisibleRegionsWereDirtyThisFrame = false; - mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence); + mTransactionCallbackInvoker.addPresentFence(std::move(presentFence)); mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); mTransactionCallbackInvoker.clearCompletedTransactions(); - if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON && - mPreviousPresentFences[0].fenceTime->isValid()) { - mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime); + mTimeStats->incrementTotalFrames(); + mTimeStats->setPresentFenceGlobal(presentFenceTime); + + const bool isInternalDisplay = display && + FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays) + .get(display->getPhysicalId()) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + + if (isInternalDisplay && display && display->getPowerMode() == hal::PowerMode::ON && + presentFenceTime->isValid()) { + mScheduler->addPresentFence(std::move(presentFenceTime)); } const bool isDisplayConnected = @@ -2564,24 +2615,6 @@ void SurfaceFlinger::postComposition() { } } - if (mAnimCompositionPending) { - mAnimCompositionPending = false; - - if (mPreviousPresentFences[0].fenceTime->isValid()) { - mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime); - } else if (isDisplayConnected) { - // The HWC doesn't support present fences, so use the refresh - // timestamp instead. - const nsecs_t presentTime = display->getRefreshTimestamp(); - mAnimFrameTracker.setActualPresentTime(presentTime); - } - mAnimFrameTracker.advanceFrame(); - } - - mTimeStats->incrementTotalFrames(); - - mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime); - const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle); const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle); mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections); @@ -2591,21 +2624,6 @@ void SurfaceFlinger::postComposition() { return; } - nsecs_t currentTime = systemTime(); - if (mHasPoweredOff) { - mHasPoweredOff = false; - } else { - nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime; - size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod); - if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) { - getBE().mFrameBuckets[numPeriods] += elapsedTime; - } else { - getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime; - } - getBE().mTotalTime += elapsedTime; - } - getBE().mLastSwapTime = currentTime; - // Cleanup any outstanding resources due to rendering a prior frame. getRenderEngine().cleanupPostRender(); @@ -2632,6 +2650,8 @@ void SurfaceFlinger::postComposition() { // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize()); } + + logFrameStats(presentTime); } FloatRect SurfaceFlinger::getMaxDisplayBounds() { @@ -2664,16 +2684,6 @@ void SurfaceFlinger::computeLayerBounds() { } } -void SurfaceFlinger::postFrame() { - const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); - if (display && getHwComposer().isConnected(display->getPhysicalId())) { - uint32_t flipCount = display->getPageFlipCount(); - if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { - logFrameStats(); - } - } -} - void SurfaceFlinger::commitTransactions() { ATRACE_CALL(); @@ -2706,10 +2716,9 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( do { hwcModes = getHwComposer().getModes(displayId); activeModeHwcId = getHwComposer().getActiveMode(displayId); - LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode"); const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) { - return mode.hwcId == *activeModeHwcId; + return mode.hwcId == activeModeHwcId; }; if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) { @@ -2717,16 +2726,21 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( } } while (++attempt < kMaxAttempts); - LOG_ALWAYS_FATAL_IF(attempt == kMaxAttempts, - "After %d attempts HWC still returns an active mode which is not" - " supported. Active mode ID = %" PRIu64 ". Supported modes = %s", - kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str()); - - DisplayModes oldModes; - if (const auto token = getPhysicalDisplayTokenLocked(displayId)) { - oldModes = getDisplayDeviceLocked(token)->getSupportedModes(); + if (attempt == kMaxAttempts) { + const std::string activeMode = + activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s; + ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, " + "hwcModes={%s}", + activeMode.c_str(), base::Join(hwcModes, ", ").c_str()); + return {}; } + const DisplayModes oldModes = mPhysicalDisplays.get(displayId) + .transform([](const PhysicalDisplay& display) { + return display.snapshot().displayModes(); + }) + .value_or(DisplayModes{}); + ui::DisplayModeId nextModeId = 1 + std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1), [](ui::DisplayModeId max, const auto& pair) { @@ -2764,70 +2778,96 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( return {modes, activeMode}; } -void SurfaceFlinger::processDisplayHotplugEventsLocked() { - for (const auto& event : mPendingHotplugEvents) { - std::optional<DisplayIdentificationInfo> info = - getHwComposer().onHotplug(event.hwcDisplayId, event.connection); +bool SurfaceFlinger::configureLocked() { + std::vector<HotplugEvent> events; + { + std::lock_guard<std::mutex> lock(mHotplugMutex); + events = std::move(mPendingHotplugEvents); + } - if (!info) { - continue; + for (const auto [hwcDisplayId, connection] : events) { + if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) { + const auto displayId = info->id; + const bool connected = connection == hal::Connection::CONNECTED; + + if (const char* const log = + processHotplug(displayId, hwcDisplayId, connected, std::move(*info))) { + ALOGI("%s display %s (HAL ID %" PRIu64 ")", log, to_string(displayId).c_str(), + hwcDisplayId); + } } + } - const auto displayId = info->id; - const auto token = mPhysicalDisplayTokens.get(displayId); + return !events.empty(); +} - if (event.connection == hal::Connection::CONNECTED) { - auto [supportedModes, activeMode] = loadDisplayModes(displayId); +const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, + hal::HWDisplayId hwcDisplayId, bool connected, + DisplayIdentificationInfo&& info) { + const auto displayOpt = mPhysicalDisplays.get(displayId); + if (!connected) { + LOG_ALWAYS_FATAL_IF(!displayOpt); + const auto& display = displayOpt->get(); - if (!token) { - ALOGV("Creating display %s", to_string(displayId).c_str()); + if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) { + mCurrentState.displays.removeItemsAt(index); + } - DisplayDeviceState state; - state.physical = {.id = displayId, - .type = getHwComposer().getDisplayConnectionType(displayId), - .hwcDisplayId = event.hwcDisplayId, - .deviceProductInfo = std::move(info->deviceProductInfo), - .supportedModes = std::move(supportedModes), - .activeMode = std::move(activeMode)}; - state.isSecure = true; // All physical displays are currently considered secure. - state.displayName = std::move(info->name); + mPhysicalDisplays.erase(displayId); + return "Disconnecting"; + } - sp<IBinder> token = new BBinder(); - mCurrentState.displays.add(token, state); - mPhysicalDisplayTokens.try_emplace(displayId, std::move(token)); - mInterceptor->saveDisplayCreation(state); - } else { - ALOGV("Recreating display %s", to_string(displayId).c_str()); - - auto& state = mCurrentState.displays.editValueFor(token->get()); - state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId. - state.physical->supportedModes = std::move(supportedModes); - state.physical->activeMode = std::move(activeMode); - if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) { - state.physical->deviceProductInfo = std::move(info->deviceProductInfo); - } - } - } else { - ALOGV("Removing display %s", to_string(displayId).c_str()); + auto [displayModes, activeMode] = loadDisplayModes(displayId); + if (!activeMode) { + // TODO(b/241286153): Report hotplug failure to the framework. + ALOGE("Failed to hotplug display %s", to_string(displayId).c_str()); + getHwComposer().disconnectDisplay(displayId); + return nullptr; + } - if (const ssize_t index = mCurrentState.displays.indexOfKey(token->get()); index >= 0) { - const DisplayDeviceState& state = mCurrentState.displays.valueAt(index); - mInterceptor->saveDisplayDeletion(state.sequenceId); - mCurrentState.displays.removeItemsAt(index); - } + ui::ColorModes colorModes = getHwComposer().getColorModes(displayId); - mPhysicalDisplayTokens.erase(displayId); + if (displayOpt) { + const auto& display = displayOpt->get(); + const auto& snapshot = display.snapshot(); + + std::optional<DeviceProductInfo> deviceProductInfo; + if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) { + deviceProductInfo = std::move(info.deviceProductInfo); + } else { + deviceProductInfo = snapshot.deviceProductInfo(); } - processDisplayChangesLocked(); + const auto it = + mPhysicalDisplays.try_replace(displayId, display.token(), displayId, + snapshot.connectionType(), std::move(displayModes), + std::move(colorModes), std::move(deviceProductInfo)); + + auto& state = mCurrentState.displays.editValueFor(it->second.token()); + state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId. + state.physical->activeMode = std::move(activeMode); + return "Reconnecting"; } - mPendingHotplugEvents.clear(); + const sp<IBinder> token = sp<BBinder>::make(); + + mPhysicalDisplays.try_emplace(displayId, token, displayId, + getHwComposer().getDisplayConnectionType(displayId), + std::move(displayModes), std::move(colorModes), + std::move(info.deviceProductInfo)); + + DisplayDeviceState state; + state.physical = {.id = displayId, + .hwcDisplayId = hwcDisplayId, + .activeMode = std::move(activeMode)}; + state.isSecure = true; // All physical displays are currently considered secure. + state.displayName = std::move(info.name); + + mCurrentState.displays.add(token, state); + return "Connecting"; } void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) { - ALOGI("Dispatching display hotplug event displayId=%s, connected=%d", - to_string(displayId).c_str(), connected); mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected); mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected); } @@ -2838,7 +2878,8 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) { - DisplayDeviceCreationArgs creationArgs(this, getHwComposer(), displayToken, compositionDisplay); + DisplayDeviceCreationArgs creationArgs(sp<SurfaceFlinger>::fromExisting(this), getHwComposer(), + displayToken, compositionDisplay); creationArgs.sequenceId = state.sequenceId; creationArgs.isSecure = state.isSecure; creationArgs.displaySurface = displaySurface; @@ -2846,37 +2887,58 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.supportedPerFrameMetadata = 0; if (const auto& physical = state.physical) { - creationArgs.connectionType = physical->type; - creationArgs.supportedModes = physical->supportedModes; creationArgs.activeModeId = physical->activeMode->getId(); const auto [kernelIdleTimerController, idleTimerTimeoutMs] = getKernelIdleTimerProperties(compositionDisplay->getId()); - scheduler::RefreshRateConfigs::Config config = - {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false), + const auto enableFrameRateOverride = [&] { + using Config = scheduler::RefreshRateSelector::Config; + if (!sysprop::enable_frame_rate_override(false)) { + return Config::FrameRateOverride::Disabled; + } + + if (sysprop::frame_rate_override_for_native_rates(true)) { + return Config::FrameRateOverride::AppOverrideNativeRefreshRates; + } + + if (!sysprop::frame_rate_override_global(false)) { + return Config::FrameRateOverride::AppOverride; + } + + return Config::FrameRateOverride::Enabled; + }(); + + scheduler::RefreshRateSelector::Config config = + {.enableFrameRateOverride = enableFrameRateOverride, .frameRateMultipleThreshold = base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0), .idleTimerTimeout = idleTimerTimeoutMs, .kernelIdleTimerController = kernelIdleTimerController}; - creationArgs.refreshRateConfigs = - std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes, - creationArgs.activeModeId, config); - } - if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) { - creationArgs.isPrimary = id == getPrimaryDisplayIdLocked(); + creationArgs.refreshRateSelector = + mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::snapshotRef) + .transform([&](const display::DisplaySnapshot& snapshot) { + return std::make_shared< + scheduler::RefreshRateSelector>(snapshot.displayModes(), + creationArgs.activeModeId, + config); + }) + .value_or(nullptr); - if (useColorManagement) { - std::vector<ColorMode> modes = getHwComposer().getColorModes(*id); - for (ColorMode colorMode : modes) { - if (isWideColorMode(colorMode)) { - creationArgs.hasWideColorGamut = true; - } + creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked(); - std::vector<RenderIntent> renderIntents = - getHwComposer().getRenderIntents(*id, colorMode); - creationArgs.hwcColorModes.emplace(colorMode, renderIntents); - } + if (useColorManagement) { + mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::snapshotRef) + .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { + for (const auto mode : snapshot.colorModes()) { + creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode); + creationArgs.hwcColorModes + .emplace(mode, + getHwComposer().getRenderIntents(physical->id, mode)); + } + })); } } @@ -2908,23 +2970,29 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( nativeWindowSurface->preallocateBuffers(); - ColorMode defaultColorMode = ColorMode::NATIVE; + ui::ColorMode defaultColorMode = ui::ColorMode::NATIVE; Dataspace defaultDataSpace = Dataspace::UNKNOWN; if (display->hasWideColorGamut()) { - defaultColorMode = ColorMode::SRGB; + defaultColorMode = ui::ColorMode::SRGB; defaultDataSpace = Dataspace::V0_SRGB; } display->getCompositionDisplay()->setColorProfile( compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN}); - if (!state.isVirtual()) { - FTL_FAKE_GUARD(kMainThreadContext, - display->setActiveMode(state.physical->activeMode->getId())); - display->setDeviceProductInfo(state.physical->deviceProductInfo); + + if (const auto& physical = state.physical) { + mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::snapshotRef) + .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { + FTL_FAKE_GUARD(kMainThreadContext, + display->setActiveMode(physical->activeMode->getId(), + physical->activeMode->getFps(), + physical->activeMode->getFps())); + })); } - display->setLayerStack(state.layerStack); + display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack)); display->setProjection(state.orientation, state.layerStackSpaceRect, state.orientedDisplaySpaceRect); display->setDisplayName(state.displayName); @@ -3000,11 +3068,18 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, LOG_FATAL_IF(!displaySurface); auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state, displaySurface, producer); - if (display->isPrimary()) { - initScheduler(display); - } - if (!state.isVirtual()) { - dispatchDisplayHotplugEvent(display->getPhysicalId(), true); + + if (mScheduler && !display->isVirtual()) { + const auto displayId = display->getPhysicalId(); + { + // TODO(b/241285876): Annotate `processDisplayAdded` instead. + ftl::FakeGuard guard(kMainThreadContext); + + // For hotplug reconnect, renew the registration since display modes have been reloaded. + mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector()); + } + + dispatchDisplayHotplugEvent(displayId, true); } mDisplays.try_emplace(displayToken, std::move(display)); @@ -3019,6 +3094,7 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { releaseVirtualDisplay(display->getVirtualId()); } else { dispatchDisplayHotplugEvent(display->getPhysicalId(), false); + mScheduler->unregisterDisplay(display->getPhysicalId()); } } @@ -3080,7 +3156,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (const auto display = getDisplayDeviceLocked(displayToken)) { if (currentState.layerStack != drawingState.layerStack) { - display->setLayerStack(currentState.layerStack); + display->setLayerFilter( + makeLayerFilterForDisplay(display->getId(), currentState.layerStack)); } if (currentState.flags != drawingState.flags) { display->setFlags(currentState.flags); @@ -3090,7 +3167,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) { display->setProjection(currentState.orientation, currentState.layerStackSpaceRect, currentState.orientedDisplaySpaceRect); - if (isDisplayActiveLocked(display)) { + if (display->getId() == mActiveDisplayId) { mActiveDisplayTransformHint = display->getTransformHint(); } } @@ -3098,15 +3175,16 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, currentState.height != drawingState.height) { display->setDisplaySize(currentState.width, currentState.height); - if (isDisplayActiveLocked(display)) { + if (display->getId() == mActiveDisplayId) { onActiveDisplaySizeChanged(display); } } } } + void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) { mVsyncConfiguration->reset(); - const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode()->getFps(); + const Fps refreshRate = activeDisplay->getActiveMode().fps; updatePhaseConfiguration(refreshRate); mRefreshRateStats->setRefreshRate(refreshRate); } @@ -3119,6 +3197,7 @@ void SurfaceFlinger::processDisplayChangesLocked() { const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays); if (!curr.isIdenticalTo(draw)) { mVisibleRegionsDirty = true; + mUpdateInputInfo = true; // find the displays that were removed // (ie: in drawing state but not in current state) @@ -3156,13 +3235,17 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; if (displayTransactionNeeded) { processDisplayChangesLocked(); - processDisplayHotplugEventsLocked(); + mFrontEndDisplayInfos.clear(); + for (const auto& [_, display] : mDisplays) { + mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo()); + } } mForceTransactionDisplayChange = displayTransactionNeeded; if (mSomeChildrenChanged) { mVisibleRegionsDirty = true; mSomeChildrenChanged = false; + mUpdateInputInfo = true; } // Update transform hint. @@ -3207,14 +3290,19 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { if (!hintDisplay) { // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to // redraw after transform hint changes. See bug 8508397. - // could be null when this layer is using a layerStack // that is not visible on any display. Also can occur at // screen off/on times. - hintDisplay = getDefaultDisplayDeviceLocked(); + // U Update: Don't provide stale hints to the clients. For + // special cases where we want the app to draw its + // first frame before the display is available, we rely + // on WMS and DMS to provide the right information + // so the client can calculate the hint. + ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName()); + layer->skipReportingTransformHint(); + } else { + layer->updateTransformHint(hintDisplay->getTransformHint()); } - - layer->updateTransformHint(hintDisplay->getTransformHint()); }); } @@ -3222,6 +3310,7 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { mLayersAdded = false; // Layers have been added. mVisibleRegionsDirty = true; + mUpdateInputInfo = true; } // some layers might have been removed, so @@ -3229,19 +3318,18 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { if (mLayersRemoved) { mLayersRemoved = false; mVisibleRegionsDirty = true; + mUpdateInputInfo = true; mDrawingState.traverseInZOrder([&](Layer* layer) { - if (mLayersPendingRemoval.indexOf(layer) >= 0) { + if (mLayersPendingRemoval.indexOf(sp<Layer>::fromExisting(layer)) >= 0) { // this layer is not visible anymore Region visibleReg; visibleReg.set(layer->getScreenBounds()); - invalidateLayerStack(layer, visibleReg); + invalidateLayerStack(sp<Layer>::fromExisting(layer), visibleReg); } }); } doCommitTransactions(); - signalSynchronousTransactions(CountDownLatch::eSyncTransaction); - mAnimTransactionPending = false; } void SurfaceFlinger::updateInputFlinger() { @@ -3253,14 +3341,14 @@ void SurfaceFlinger::updateInputFlinger() { std::vector<WindowInfo> windowInfos; std::vector<DisplayInfo> displayInfos; bool updateWindowInfo = false; - if (mVisibleRegionsDirty || mInputInfoChanged) { - mInputInfoChanged = false; + if (mUpdateInputInfo) { + mUpdateInputInfo = false; updateWindowInfo = true; buildWindowInfos(windowInfos, displayInfos); - } - if (!updateWindowInfo && mInputWindowCommands.empty()) { + } else if (mInputWindowCommands.empty()) { return; } + BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo, windowInfos = std::move(windowInfos), displayInfos = std::move(displayInfos), @@ -3269,12 +3357,17 @@ void SurfaceFlinger::updateInputFlinger() { inputFlinger = mInputFlinger, this]() { ATRACE_NAME("BackgroundExecutor::updateInputFlinger"); if (updateWindowInfo) { - mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, - inputWindowCommands.syncInputWindows); - } else if (inputWindowCommands.syncInputWindows) { - // If the caller requested to sync input windows, but there are no - // changes to input windows, notify immediately. - windowInfosReported(); + mWindowInfosListenerInvoker + ->windowInfosChanged(windowInfos, displayInfos, + inputWindowCommands.windowInfosReportedListeners); + } else { + // If there are listeners but no changes to input windows, call the listeners + // immediately. + for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) { + if (IInterface::asBinder(listener)->isBinderAlive()) { + listener->onWindowInfosReported(); + } + } } for (const auto& focusRequest : inputWindowCommands.focusRequests) { inputFlinger->setFocusedWindow(focusRequest); @@ -3306,7 +3399,7 @@ void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) { ALOGE_IF(error != NO_ERROR, "Error setting display brightness for display %s: %d (%s)", - display->getDebugName().c_str(), error, strerror(error)); + to_string(display->getId()).c_str(), error, strerror(error)); } display->persistBrightness(needsComposite); } @@ -3315,29 +3408,6 @@ void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) { void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, std::vector<DisplayInfo>& outDisplayInfos) { - ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos; - - for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { - const auto layerStack = display->getLayerStack(); - const auto info = display->getInputInfo(); - - const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info); - if (emplaced) { - continue; - } - - // If the layer stack is mirrored on multiple displays, the first display that is configured - // to receive input takes precedence. - auto& otherInfo = it->second; - if (otherInfo.receivesInput) { - ALOGW_IF(display->receivesInput(), - "Multiple displays claim to accept input for the same layer stack: %u", - layerStack.id); - } else { - otherInfo = info; - } - } - static size_t sNumWindowInfos = 0; outWindowInfos.reserve(sNumWindowInfos); sNumWindowInfos = 0; @@ -3345,17 +3415,18 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - const auto opt = displayInputInfos.get(layer->getLayerStack(), - [](const auto& info) -> Layer::InputDisplayArgs { - return {&info.transform, info.isSecure}; - }); + const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack()) + .transform([](const frontend::DisplayInfo& info) { + return Layer::InputDisplayArgs{&info.transform, info.isSecure}; + }); + outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{}))); }); sNumWindowInfos = outWindowInfos.size(); - outDisplayInfos.reserve(displayInputInfos.size()); - for (const auto& [_, info] : displayInputInfos) { + outDisplayInfos.reserve(mFrontEndDisplayInfos.size()); + for (const auto& [_, info] : mFrontEndDisplayInfos) { outDisplayInfos.push_back(info.info); } } @@ -3368,28 +3439,45 @@ void SurfaceFlinger::updateCursorAsync() { } } + std::vector<LayerSnapshotGuard> layerSnapshotGuards; + mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) { + if (layer->getLayerSnapshot()->compositionType == + aidl::android::hardware::graphics::composer3::Composition::CURSOR) { + layer->updateSnapshot(false /* updateGeometry */); + layerSnapshotGuards.emplace_back(layer); + } + }); + mCompositionEngine->updateCursorAsync(refreshArgs); } -void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) { +void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) { + if (mBootStage != BootStage::FINISHED) { + ALOGV("Currently in the boot stage, skipping display mode changes"); + return; + } + + ATRACE_CALL(); + // If this is called from the main thread mStateLock must be locked before // Currently the only way to call this function from the main thread is from // Scheduler::chooseRefreshRateForContent ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - const auto display = getDefaultDisplayDeviceLocked(); - if (!display || mBootStage != BootStage::FINISHED) { - return; - } - ATRACE_CALL(); + for (auto& request : modeRequests) { + const auto& modePtr = request.mode.modePtr; + const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId()); - if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) { - ALOGV("Skipping disallowed mode %d", mode->getId().value()); - return; - } + if (!display) continue; - setDesiredActiveMode({std::move(mode), event}); + if (display->refreshRateSelector().isModeAllowed(request.mode)) { + setDesiredActiveMode(std::move(request)); + } else { + ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(), + to_string(display->getId()).c_str()); + } + } } void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { @@ -3401,20 +3489,16 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); } -void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { - if (mScheduler) { - // If the scheduler is already initialized, this means that we received - // a hotplug(connected) on the primary display. In that case we should - // update the scheduler with the most recent display information. - ALOGW("Scheduler already initialized, updating instead"); - mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs()); - return; - } - const auto currRefreshRate = display->getActiveMode()->getFps(); - mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate, - hal::PowerMode::OFF); +void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { + LOG_ALWAYS_FATAL_IF(mScheduler); - mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate); + const auto activeMode = display->refreshRateSelector().getActiveMode(); + const Fps activeRefreshRate = activeMode.fps; + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate, + hal::PowerMode::OFF); + + mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate); mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); using Feature = scheduler::Feature; @@ -3430,51 +3514,37 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) { features |= Feature::kPresentFences; } + if (display->refreshRateSelector().kernelIdleTimerController()) { + features |= Feature::kKernelIdleTimer; + } mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features); - { - auto configs = display->holdRefreshRateConfigs(); - if (configs->kernelIdleTimerController().has_value()) { - features |= Feature::kKernelIdleTimer; - } + mScheduler->createVsyncSchedule(features); + mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); - mScheduler->createVsyncSchedule(features); - mScheduler->setRefreshRateConfigs(std::move(configs)); - } setVsyncEnabled(false); mScheduler->startTimers(); const auto configs = mVsyncConfiguration->getCurrentConfigs(); - const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs(); + const nsecs_t vsyncPeriod = activeRefreshRate.getPeriodNsecs(); mAppConnectionHandle = mScheduler->createConnection("app", mFrameTimeline->getTokenManager(), /*workDuration=*/configs.late.appWorkDuration, - /*readyDuration=*/configs.late.sfWorkDuration, - impl::EventThread::InterceptVSyncsCallback()); + /*readyDuration=*/configs.late.sfWorkDuration); mSfConnectionHandle = mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(), /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod), - /*readyDuration=*/configs.late.sfWorkDuration, - [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }); + /*readyDuration=*/configs.late.sfWorkDuration); - mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(), - configs.late.sfWorkDuration); + mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), + *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); mRegionSamplingThread = - new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables()); - mFpsReporter = new FpsReporter(*mFrameTimeline, *this); - // Dispatch a mode change request for the primary display on scheduler - // initialization, so that the EventThreads always contain a reference to a - // prior configuration. - // - // This is a bit hacky, but this avoids a back-pointer into the main SF - // classes from EventThread, and there should be no run-time binder cost - // anyway since there are no connected apps at this point. - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode()); + sp<RegionSamplingThread>::make(*this, + RegionSamplingThread::EnvironmentTimingTunables()); + mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); } void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) { @@ -3524,10 +3594,6 @@ void SurfaceFlinger::doCommitTransactions() { mLayersPendingRemoval.clear(); } - // If this transaction is part of a window animation then the next frame - // we composite should be considered an animation as well. - mAnimCompositionPending = mAnimTransactionPending; - mDrawingState = mCurrentState; // clear the "changed" flags in current state mCurrentState.colorMatrixChanged = false; @@ -3573,8 +3639,6 @@ bool SurfaceFlinger::latchBuffers() { bool frameQueued = false; bool newDataLatched = false; - const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); - // Store the set of layers that need updates. This set must not change as // buffers are being latched, as this could result in a deadlock. // Example: Two producers share the same command stream and: @@ -3594,12 +3658,7 @@ bool SurfaceFlinger::latchBuffers() { if (layer->hasReadyFrame()) { frameQueued = true; - if (layer->shouldPresentNow(expectedPresentTime)) { - mLayersWithQueuedFrames.emplace(layer); - } else { - ATRACE_NAME("!layer->shouldPresentNow()"); - layer->useEmptyDamage(); - } + mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer)); } else { layer->useEmptyDamage(); } @@ -3620,7 +3679,7 @@ bool SurfaceFlinger::latchBuffers() { Mutex::Autolock lock(mStateLock); for (const auto& layer : mLayersWithQueuedFrames) { - if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) { + if (layer->latchBuffer(visibleRegions, latchTime)) { mLayersPendingRefresh.push_back(layer); newDataLatched = true; } @@ -3651,12 +3710,12 @@ bool SurfaceFlinger::latchBuffers() { return !mLayersWithQueuedFrames.empty() && newDataLatched; } -status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, +status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parent, - bool addToRoot, uint32_t* outTransformHint) { - if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) { + uint32_t* outTransformHint) { + if (mNumLayers >= MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), - ISurfaceComposer::MAX_LAYERS); + MAX_LAYERS); static_cast<void>(mScheduler->schedule([=] { ALOGE("Dumping random sampling of on-screen layers: "); mDrawingState.traverse([&](Layer *layer) { @@ -3677,18 +3736,14 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind return NO_MEMORY; } - { - std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - mCreatedLayers.emplace_back(layer, parent, addToRoot); - } - layer->updateTransformHint(mActiveDisplayTransformHint); if (outTransformHint) { *outTransformHint = mActiveDisplayTransformHint; } - // attach this layer to the client - if (client != nullptr) { - client->attachLayer(handle, layer); + + { + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + mCreatedLayers.emplace_back(layer, parent, args.addToRoot); } setTransactionFlags(eTransactionNeeded); @@ -3712,194 +3767,125 @@ void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule sche } } -bool SurfaceFlinger::stopTransactionProcessing( - const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& - applyTokensWithUnsignaledTransactions) const { - if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) { - // if we are in LatchUnsignaledConfig::AutoSingleLayer - // then we should have only one applyToken for processing. - // so we can stop further transactions on this applyToken. - return !applyTokensWithUnsignaledTransactions.empty(); - } - - return false; -} - -int SurfaceFlinger::flushUnsignaledPendingTransactionQueues( - std::vector<TransactionState>& transactions, - std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions) { - return flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions, - /*tryApplyUnsignaled*/ true); -} - -int SurfaceFlinger::flushPendingTransactionQueues( - std::vector<TransactionState>& transactions, - std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions, - bool tryApplyUnsignaled) { - int transactionsPendingBarrier = 0; - auto it = mPendingTransactionQueues.begin(); - while (it != mPendingTransactionQueues.end()) { - auto& [applyToken, transactionQueue] = *it; - while (!transactionQueue.empty()) { - if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) { - ATRACE_NAME("stopTransactionProcessing"); - break; - } +TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck( + const TransactionHandler::TransactionFlushState& flushState) { + using TransactionReadiness = TransactionHandler::TransactionReadiness; + const auto& transaction = *flushState.transaction; + ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, + transaction.frameTimelineInfo.vsyncId); + TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime); + // 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 (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime && + desiredPresentTime < mExpectedPresentTime + 1s) { + ATRACE_NAME("not current"); + return TransactionReadiness::NotReady; + } - auto& transaction = transactionQueue.front(); - const auto ready = - transactionIsReadyToBeApplied(transaction, - transaction.frameTimelineInfo, - transaction.isAutoTimestamp, - transaction.desiredPresentTime, - transaction.originUid, transaction.states, - bufferLayersReadyToPresent, transactions.size(), - tryApplyUnsignaled); - ATRACE_INT("TransactionReadiness", static_cast<int>(ready)); - if (ready == TransactionReadiness::NotReady) { - setTransactionFlags(eTransactionFlushNeeded); - break; - } - if (ready == TransactionReadiness::NotReadyBarrier) { - transactionsPendingBarrier++; - setTransactionFlags(eTransactionFlushNeeded); - break; + if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) { + ATRACE_NAME("!isVsyncValid"); + return TransactionReadiness::NotReady; + } + + // If the client didn't specify desiredPresentTime, use the vsyncId to determine the + // expected present time of this transaction. + if (transaction.isAutoTimestamp && + frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) { + ATRACE_NAME("frameIsEarly"); + return TransactionReadiness::NotReady; + } + return TransactionReadiness::Ready; +} + +TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck( + const TransactionHandler::TransactionFlushState& flushState) { + using TransactionReadiness = TransactionHandler::TransactionReadiness; + auto ready = TransactionReadiness::Ready; + flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool { + sp<Layer> layer = LayerHandle::getLayer(s.surface); + const auto& transaction = *flushState.transaction; + // check for barrier frames + if (s.bufferData->hasBarrier && + ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) { + const bool willApplyBarrierFrame = + flushState.bufferLayersReadyToPresent.contains(s.surface.get()) && + (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >= + s.bufferData->barrierFrameNumber); + if (!willApplyBarrierFrame) { + ATRACE_NAME("NotReadyBarrier"); + ready = TransactionReadiness::NotReadyBarrier; + return false; } - transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { - const bool frameNumberChanged = state.bufferData->flags.test( - BufferData::BufferDataChange::frameNumberChanged); - if (frameNumberChanged) { - bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber; - } else { - // Barrier function only used for BBQ which always includes a frame number - bufferLayersReadyToPresent[state.surface] = - std::numeric_limits<uint64_t>::max(); + } + + // If backpressure is enabled and we already have a buffer to commit, keep + // the transaction in the queue. + const bool hasPendingBuffer = + flushState.bufferLayersReadyToPresent.contains(s.surface.get()); + if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) { + ATRACE_NAME("hasPendingBuffer"); + ready = TransactionReadiness::NotReady; + return false; + } + + // check fence status + const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(), + flushState.firstTransaction); + ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(), + allowLatchUnsignaled ? "true" : "false"); + + const bool acquireFenceChanged = s.bufferData && + s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) && + s.bufferData->acquireFence; + const bool fenceSignaled = + (!acquireFenceChanged || + s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled); + if (!fenceSignaled) { + if (!allowLatchUnsignaled) { + ready = TransactionReadiness::NotReady; + auto& listener = s.bufferData->releaseBufferListener; + if (listener && + (flushState.queueProcessTime - transaction.postTime) > + std::chrono::nanoseconds(4s).count()) { + mTransactionHandler + .onTransactionQueueStalled(transaction.id, listener, + "Buffer processing hung up due to stuck " + "fence. Indicates GPU hang"); } - }); - const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled); - if (appliedUnsignaled) { - applyTokensWithUnsignaledTransactions.insert(transaction.applyToken); + return false; } - transactions.emplace_back(std::move(transaction)); - transactionQueue.pop(); + ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer + ? TransactionReadiness::ReadyUnsignaledSingle + : TransactionReadiness::ReadyUnsignaled; } + return true; + }); + ATRACE_INT("TransactionReadiness", static_cast<int>(ready)); + return ready; +} - if (transactionQueue.empty()) { - it = mPendingTransactionQueues.erase(it); - mTransactionQueueCV.broadcast(); - } else { - it = std::next(it, 1); - } - } - return transactionsPendingBarrier; +void SurfaceFlinger::addTransactionReadyFilters() { + mTransactionHandler.addTransactionReadyFilter( + std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1)); + mTransactionHandler.addTransactionReadyFilter( + std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1)); } -bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { +bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) { // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer // states) around outside the scope of the lock - std::vector<TransactionState> transactions; - // Layer handles that have transactions with buffers that are ready to be applied. - std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent; - std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions; + std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions(); { Mutex::Autolock _l(mStateLock); - { - Mutex::Autolock _l(mQueueLock); - - int lastTransactionsPendingBarrier = 0; - int transactionsPendingBarrier = 0; - // First collect transactions from the pending transaction queues. - // We are not allowing unsignaled buffers here as we want to - // collect all the transactions from applyTokens that are ready first. - transactionsPendingBarrier = - flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false); - - // Second, collect transactions from the transaction queue. - // Here as well we are not allowing unsignaled buffers for the same - // reason as above. - while (!mTransactionQueue.empty()) { - auto& transaction = mTransactionQueue.front(); - const bool pendingTransactions = - mPendingTransactionQueues.find(transaction.applyToken) != - mPendingTransactionQueues.end(); - const auto ready = [&]() REQUIRES(mStateLock) { - if (pendingTransactions) { - ATRACE_NAME("pendingTransactions"); - return TransactionReadiness::NotReady; - } - - return transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo, - transaction.isAutoTimestamp, - transaction.desiredPresentTime, - transaction.originUid, transaction.states, - bufferLayersReadyToPresent, - transactions.size(), - /*tryApplyUnsignaled*/ false); - }(); - ATRACE_INT("TransactionReadiness", static_cast<int>(ready)); - if (ready != TransactionReadiness::Ready) { - if (ready == TransactionReadiness::NotReadyBarrier) { - transactionsPendingBarrier++; - } - mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction)); - } else { - transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { - const bool frameNumberChanged = state.bufferData->flags.test( - BufferData::BufferDataChange::frameNumberChanged); - if (frameNumberChanged) { - bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber; - } else { - // Barrier function only used for BBQ which always includes a frame number. - // This value only used for barrier logic. - bufferLayersReadyToPresent[state.surface] = - std::numeric_limits<uint64_t>::max(); - } - }); - transactions.emplace_back(std::move(transaction)); - } - mTransactionQueue.pop_front(); - ATRACE_INT("TransactionQueue", mTransactionQueue.size()); - } - - // Transactions with a buffer pending on a barrier may be on a different applyToken - // than the transaction which satisfies our barrier. In fact this is the exact use case - // that the primitive is designed for. This means we may first process - // the barrier dependent transaction, determine it ineligible to complete - // and then satisfy in a later inner iteration of flushPendingTransactionQueues. - // The barrier dependent transaction was eligible to be presented in this frame - // but we would have prevented it without case. To fix this we continually - // loop through flushPendingTransactionQueues until we perform an iteration - // where the number of transactionsPendingBarrier doesn't change. This way - // we can continue to resolve dependency chains of barriers as far as possible. - while (lastTransactionsPendingBarrier != transactionsPendingBarrier) { - lastTransactionsPendingBarrier = transactionsPendingBarrier; - transactionsPendingBarrier = - flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions, - /*tryApplyUnsignaled*/ false); - } - - // We collected all transactions that could apply without latching unsignaled buffers. - // If we are allowing latch unsignaled of some form, now it's the time to go over the - // transactions that were not applied and try to apply them unsignaled. - if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { - flushUnsignaledPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions); - } - - return applyTransactions(transactions, vsyncId); - } + return applyTransactions(transactions, vsyncId); } } bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions, - int64_t vsyncId) { + VsyncId vsyncId) { bool needsTraversal = false; // Now apply all transactions. for (auto& transaction : transactions) { @@ -3912,46 +3898,42 @@ bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactio transaction.permissions, transaction.hasListenerCallbacks, transaction.listenerCallbacks, transaction.originPid, transaction.originUid, transaction.id); - if (transaction.transactionCommittedSignal) { - mTransactionCommittedSignals.emplace_back( - std::move(transaction.transactionCommittedSignal)); - } } if (mTransactionTracing) { - mTransactionTracing->addCommittedTransactions(transactions, vsyncId); + mTransactionTracing->addCommittedTransactions(transactions, vsyncId.value); } return needsTraversal; } bool SurfaceFlinger::transactionFlushNeeded() { - Mutex::Autolock _l(mQueueLock); - return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty(); + return mTransactionHandler.hasPendingTransactions(); } -bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const { - // The amount of time SF can delay a frame if it is considered early based - // on the VsyncModulator::VsyncConfig::appWorkDuration - constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms; - - const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod; - const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2; - - const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId); - if (!prediction.has_value()) { +bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const { + const auto prediction = + mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId.value); + if (!prediction) { return false; } - if (std::abs(prediction->presentTime - expectedPresentTime) >= - kEarlyLatchMaxThreshold.count()) { + const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime); + + // The duration for which SF can delay a frame if it is considered early based on the + // VsyncModulator::VsyncConfig::appWorkDuration. + if (constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms; + std::chrono::abs(predictedPresentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold) { return false; } - return prediction->presentTime >= expectedPresentTime && - prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold; + const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule().period() / 2; + + return predictedPresentTime >= expectedPresentTime && + predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold; } + bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state, - size_t numStates, size_t totalTXapplied) const { + size_t numStates, bool firstTransaction) const { if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) { ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__); return false; @@ -3970,9 +3952,9 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s } if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) { - if (totalTXapplied > 0) { - ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)", - __func__, totalTXapplied); + if (!firstTransaction) { + ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)", + __func__); return false; } @@ -3996,152 +3978,8 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s return true; } -auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction, - const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, - uid_t originUid, const Vector<ComposerState>& states, - const std::unordered_map< - sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness { - ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId); - const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); - // 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 (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime && - desiredPresentTime < expectedPresentTime + s2ns(1)) { - ATRACE_NAME("not current"); - return TransactionReadiness::NotReady; - } - - if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) { - ATRACE_NAME("!isVsyncValid"); - return TransactionReadiness::NotReady; - } - - // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected - // present time of this transaction. - if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) { - ATRACE_NAME("frameIsEarly"); - return TransactionReadiness::NotReady; - } - - bool fenceUnsignaled = false; - auto queueProcessTime = systemTime(); - for (const ComposerState& state : states) { - const layer_state_t& s = state.state; - - sp<Layer> layer = nullptr; - if (s.surface) { - layer = fromHandle(s.surface).promote(); - } else if (s.hasBufferChanges()) { - ALOGW("Transaction with buffer, but no Layer?"); - continue; - } - if (!layer) { - continue; - } - - if (s.hasBufferChanges() && s.bufferData->hasBarrier && - ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) { - const bool willApplyBarrierFrame = - (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) && - (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber); - if (!willApplyBarrierFrame) { - ATRACE_NAME("NotReadyBarrier"); - return TransactionReadiness::NotReadyBarrier; - } - } - - const bool allowLatchUnsignaled = tryApplyUnsignaled && - shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied); - ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(), - allowLatchUnsignaled ? "true" : "false"); - - const bool acquireFenceChanged = s.bufferData && - s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) && - s.bufferData->acquireFence; - fenceUnsignaled = fenceUnsignaled || - (acquireFenceChanged && - s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled); - - if (fenceUnsignaled && !allowLatchUnsignaled) { - if (!transaction.sentFenceTimeoutWarning && - queueProcessTime - transaction.queueTime > std::chrono::nanoseconds(4s).count()) { - transaction.sentFenceTimeoutWarning = true; - auto listener = s.bufferData->releaseBufferListener; - if (listener) { - listener->onTransactionQueueStalled(); - } - } - - ATRACE_NAME("fence unsignaled"); - return TransactionReadiness::NotReady; - } - - if (s.hasBufferChanges()) { - // If backpressure is enabled and we already have a buffer to commit, keep the - // transaction in the queue. - const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) != - bufferLayersReadyToPresent.end(); - if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) { - ATRACE_NAME("hasPendingBuffer"); - return TransactionReadiness::NotReady; - } - } - } - return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready; -} - -void SurfaceFlinger::queueTransaction(TransactionState& state) { - state.queueTime = systemTime(); - - Mutex::Autolock lock(mQueueLock); - - // Generate a CountDownLatch pending state if this is a synchronous transaction. - if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) { - state.transactionCommittedSignal = std::make_shared<CountDownLatch>( - (state.inputWindowCommands.syncInputWindows - ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction) - : CountDownLatch::eSyncTransaction)); - } - - mTransactionQueue.emplace_back(state); - ATRACE_INT("TransactionQueue", mTransactionQueue.size()); - - const auto schedule = [](uint32_t flags) { - if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd; - if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart; - return TransactionSchedule::Late; - }(state.flags); - - const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone; - - setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint); -} - -void SurfaceFlinger::waitForSynchronousTransaction( - const CountDownLatch& transactionCommittedSignal) { - // applyTransactionState is called on the main SF thread. While a given process may wish - // to wait on synchronous transactions, the main SF thread should apply the transaction and - // set the value to notify this after committed. - if (!transactionCommittedSignal.wait_until( - std::chrono::nanoseconds(mAnimationTransactionTimeout))) { - ALOGE("setTransactionState timed out!"); - } -} - -void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) { - for (auto it = mTransactionCommittedSignals.begin(); - it != mTransactionCommittedSignals.end();) { - if ((*it)->countDown(flag)) { - it = mTransactionCommittedSignals.erase(it); - } else { - it++; - } - } -} - status_t SurfaceFlinger::setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states, + const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, @@ -4173,7 +4011,25 @@ status_t SurfaceFlinger::setTransactionState( IPCThreadState* ipc = IPCThreadState::self(); const int originPid = ipc->getCallingPid(); const int originUid = ipc->getCallingUid(); - TransactionState state{frameTimelineInfo, states, + + std::vector<ResolvedComposerState> resolvedStates; + resolvedStates.reserve(states.size()); + for (auto& state : states) { + resolvedStates.emplace_back(std::move(state)); + auto& resolvedState = resolvedStates.back(); + if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() && + resolvedState.state.surface) { + sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); + std::string layerName = (layer) ? + layer->getDebugName() : std::to_string(resolvedState.state.layerId); + resolvedState.externalTexture = + getExternalTextureFromBufferData(*resolvedState.state.bufferData, + layerName.c_str(), transactionId); + mBufferCountTracker.increment(resolvedState.state.surface->localBinder()); + } + } + + TransactionState state{frameTimelineInfo, resolvedStates, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, @@ -4182,27 +4038,26 @@ status_t SurfaceFlinger::setTransactionState( listenerCallbacks, originPid, originUid, transactionId}; - // Check for incoming buffer updates and increment the pending buffer count. - state.traverseStatesWithBuffers([&](const layer_state_t& state) { - mBufferCountTracker.increment(state.surface->localBinder()); - }); - if (mTransactionTracing) { mTransactionTracing->addQueuedTransaction(state); } - queueTransaction(state); - // Check the pending state to make sure the transaction is synchronous. - if (state.transactionCommittedSignal) { - waitForSynchronousTransaction(*state.transactionCommittedSignal); - } + const auto schedule = [](uint32_t flags) { + if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd; + if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart; + return TransactionSchedule::Late; + }(state.flags); + + const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone; + setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint); + mTransactionHandler.queueTransaction(std::move(state)); return NO_ERROR; } bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo, - Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, + std::vector<ResolvedComposerState>& states, + Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, @@ -4211,7 +4066,8 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid, uint64_t transactionId) { uint32_t transactionFlags = 0; - for (const DisplayState& display : displays) { + for (DisplayState& display : displays) { + display.sanitize(permissions); transactionFlags |= setDisplayStateLocked(display); } @@ -4223,12 +4079,12 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } uint32_t clientStateFlags = 0; - for (int i = 0; i < states.size(); i++) { - ComposerState& state = states.editItemAt(i); - clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, - isAutoTimestamp, postTime, permissions); - if ((flags & eAnimation) && state.state.surface) { - if (const auto layer = fromHandle(state.state.surface).promote()) { + for (auto& resolvedState : states) { + clientStateFlags |= + setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime, + isAutoTimestamp, postTime, permissions, transactionId); + if ((flags & eAnimation) && resolvedState.state.surface) { + if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) { using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; mScheduler->recordLayerHistory(layer.get(), isAutoTimestamp ? 0 : desiredPresentTime, @@ -4246,25 +4102,22 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } if (uncacheBuffer.isValid()) { - ClientCache::getInstance().erase(uncacheBuffer); + sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer); + if (buffer != nullptr) { + mBufferIdsToUncache.push_back(buffer->getId()); + } } // If a synchronous transaction is explicitly requested without any changes, force a transaction // anyway. This can be used as a flush mechanism for previous async transactions. // Empty animation transaction can be used to simulate back-pressure, so also force a // transaction for empty animation transactions. - if (transactionFlags == 0 && - ((flags & eSynchronous) || (flags & eAnimation))) { + if (transactionFlags == 0 && (flags & eAnimation)) { transactionFlags = eTransactionNeeded; } bool needsTraversal = false; if (transactionFlags) { - if (mInterceptor->isEnabled()) { - mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags, - originPid, originUid, transactionId); - } - // We are on the main thread, we are about to preform a traversal. Clear the traversal bit // so we don't have to wake up again next frame to preform an unnecessary traversal. if (transactionFlags & eTraversalNeeded) { @@ -4274,10 +4127,6 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin if (transactionFlags) { setTransactionFlags(transactionFlags); } - - if (flags & eAnimation) { - mAnimTransactionPending = true; - } } return needsTraversal; @@ -4350,9 +4199,10 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermis } uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo, - ComposerState& composerState, + ResolvedComposerState& composerState, int64_t desiredPresentTime, bool isAutoTimestamp, - int64_t postTime, uint32_t permissions) { + int64_t postTime, uint32_t permissions, + uint64_t transactionId) { layer_state_t& s = composerState.state; s.sanitize(permissions); @@ -4378,7 +4228,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime uint32_t flags = 0; sp<Layer> layer = nullptr; if (s.surface) { - layer = fromHandle(s.surface).promote(); + layer = LayerHandle::getLayer(s.surface); } else { // The client may provide us a null handle. Treat it as if the layer was removed. ALOGW("Attempt to set client state with a null layer handle"); @@ -4386,11 +4236,13 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (layer == nullptr) { for (auto& [listener, callbackIds] : s.listeners) { mTransactionCallbackInvoker.registerUnpresentedCallbackHandle( - new CallbackHandle(listener, callbackIds, s.surface)); + sp<CallbackHandle>::make(listener, callbackIds, s.surface)); } return 0; } + ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current); + // Only set by BLAST adapter layers if (what & layer_state_t::eProducerDisconnect) { layer->onDisconnect(); @@ -4440,18 +4292,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } } } - if (what & layer_state_t::eSizeChanged) { - if (layer->setSize(s.w, s.h)) { - flags |= eTraversalNeeded; - } - } if (what & layer_state_t::eAlphaChanged) { - if (layer->setAlpha(s.alpha)) - flags |= eTraversalNeeded; + if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded; } if (what & layer_state_t::eColorChanged) { - if (layer->setColor(s.color)) - flags |= eTraversalNeeded; + if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded; } if (what & layer_state_t::eColorTransformChanged) { if (layer->setColorTransform(s.colorTransform)) { @@ -4459,7 +4304,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } } if (what & layer_state_t::eBackgroundColorChanged) { - if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) { + if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) { flags |= eTraversalNeeded; } } @@ -4483,6 +4328,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (what & layer_state_t::eBlurRegionsChanged) { if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eRenderBorderChanged) { + if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, @@ -4503,8 +4353,8 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded; } } - if (what & layer_state_t::eTransformChanged) { - if (layer->setTransform(s.transform)) flags |= eTraversalNeeded; + if (what & layer_state_t::eBufferTransformChanged) { + if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded; } if (what & layer_state_t::eTransformToDisplayInverseChanged) { if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse)) @@ -4534,16 +4384,20 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } std::optional<nsecs_t> dequeueBufferTimestamp; if (what & layer_state_t::eMetadataChanged) { - dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME); + dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME); - if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) { + if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1); + gameMode != -1) { // The transaction will be received on the Task layer and needs to be applied to all // child layers. Child layers that are added at a later point will obtain the game mode // info through addChild(). layer->setGameModeForTree(static_cast<GameMode>(gameMode)); } - if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded; + if (layer->setMetadata(s.metadata)) { + flags |= eTraversalNeeded; + mLayerMetadataSnapshotNeeded = true; + } } if (what & layer_state_t::eColorSpaceAgnosticChanged) { if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) { @@ -4553,6 +4407,14 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (what & layer_state_t::eShadowRadiusChanged) { if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) { + const auto compatibility = + Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility); + + if (layer->setDefaultFrameRateCompatibility(compatibility)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eFrameRateSelectionPriority) { if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) { flags |= eTraversalNeeded; @@ -4603,7 +4465,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (what & layer_state_t::eDropInputModeChanged) { if (layer->setDropInputMode(s.dropInputMode)) { flags |= eTraversalNeeded; - mInputInfoChanged = true; + mUpdateInputInfo = true; } } // This has to happen after we reparent children because when we reparent to null we remove @@ -4626,15 +4488,15 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime std::vector<sp<CallbackHandle>> callbackHandles; if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) { for (auto& [listener, callbackIds] : filteredListeners) { - callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface)); + callbackHandles.emplace_back( + sp<CallbackHandle>::make(listener, callbackIds, s.surface)); } } if (what & layer_state_t::eBufferChanged) { - std::shared_ptr<renderengine::ExternalTexture> buffer = - getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName()); - if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp, - dequeueBufferTimestamp, frameTimelineInfo)) { + if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime, + desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp, + frameTimelineInfo)) { flags |= eTraversalNeeded; } } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { @@ -4644,6 +4506,13 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded; // Do not put anything that updates layer state or modifies flags after // setTransactionCompletedListener + + // if the layer has been parented on to a new display, update its transform hint. + if (((flags & eTransformHintUpdateNeeded) == 0) && + oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) { + flags |= eTransformHintUpdateNeeded; + } + return flags; } @@ -4653,21 +4522,25 @@ uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& input } status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args, - const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, - int32_t* outLayerId) { + const sp<IBinder>& mirrorFromHandle, + gui::CreateSurfaceResult& outResult) { if (!mirrorFromHandle) { return NAME_NOT_FOUND; } sp<Layer> mirrorLayer; sp<Layer> mirrorFrom; + LayerCreationArgs mirrorArgs(args); { Mutex::Autolock _l(mStateLock); - mirrorFrom = fromHandle(mirrorFromHandle).promote(); + mirrorFrom = LayerHandle::getLayer(mirrorFromHandle); if (!mirrorFrom) { return NAME_NOT_FOUND; } - status_t result = createContainerLayer(args, outHandle, &mirrorLayer); + mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill; + mirrorArgs.mirrorLayerHandle = mirrorFromHandle; + mirrorArgs.addToRoot = false; + status_t result = createEffectLayer(mirrorArgs, &outResult.handle, &mirrorLayer); if (result != NO_ERROR) { return result; } @@ -4675,43 +4548,87 @@ status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args, mirrorLayer->setClonedChild(mirrorFrom->createClone()); } - *outLayerId = mirrorLayer->sequence; + outResult.layerId = mirrorLayer->sequence; + outResult.layerName = String16(mirrorLayer->getDebugName()); if (mTransactionTracing) { - mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence, - args.name, mirrorFrom->sequence); + mTransactionTracing->onMirrorLayerAdded(outResult.handle->localBinder(), + mirrorLayer->sequence, args.name, + mirrorFrom->sequence); } - return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */, - false /* addToRoot */, nullptr /* outTransformHint */); + return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */, + nullptr /* parent */, nullptr /* outTransformHint */); } -status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, - const sp<IBinder>& parentHandle, int32_t* outLayerId, - const sp<Layer>& parentLayer, uint32_t* outTransformHint) { - ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr, - "Expected only one of parentLayer or parentHandle to be non-null. " - "Programmer error?"); +status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args, + gui::CreateSurfaceResult& outResult) { + IPCThreadState* ipc = IPCThreadState::self(); + const int uid = ipc->getCallingUid(); + if (uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != AID_SHELL) { + ALOGE("Permission denied when trying to mirror display"); + return PERMISSION_DENIED; + } + + ui::LayerStack layerStack; + sp<Layer> rootMirrorLayer; + status_t result = 0; + + { + Mutex::Autolock lock(mStateLock); + + const auto display = getDisplayDeviceLocked(displayId); + if (!display) { + return NAME_NOT_FOUND; + } + + layerStack = display->getLayerStack(); + LayerCreationArgs mirrorArgs(args); + mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill; + mirrorArgs.addToRoot = true; + result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer); + outResult.layerId = rootMirrorLayer->sequence; + outResult.layerName = String16(rootMirrorLayer->getDebugName()); + result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */, + nullptr /* parent */, nullptr /* outTransformHint */); + } + + if (result != NO_ERROR) { + return result; + } + + if (mTransactionTracing) { + mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), outResult.layerId, + args.name, args.flags, -1 /* parentId */); + } + + { + std::scoped_lock<std::mutex> lock(mMirrorDisplayLock); + mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client); + } + setTransactionFlags(eTransactionFlushNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) { status_t result = NO_ERROR; sp<Layer> layer; switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: - case ISurfaceComposerClient::eFXSurfaceBufferState: { - result = createBufferStateLayer(args, outHandle, &layer); + case ISurfaceComposerClient::eFXSurfaceContainer: + case ISurfaceComposerClient::eFXSurfaceBufferState: + args.flags |= ISurfaceComposerClient::eNoColorFill; + FMT_FALLTHROUGH; + case ISurfaceComposerClient::eFXSurfaceEffect: { + result = createBufferStateLayer(args, &outResult.handle, &layer); std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter(); if (pendingBufferCounter) { std::string counterName = layer->getPendingBufferCounterName(); - mBufferCountTracker.add((*outHandle)->localBinder(), counterName, + mBufferCountTracker.add(outResult.handle->localBinder(), counterName, pendingBufferCounter); } } break; - case ISurfaceComposerClient::eFXSurfaceEffect: - result = createEffectLayer(args, outHandle, &layer); - break; - case ISurfaceComposerClient::eFXSurfaceContainer: - result = createContainerLayer(args, outHandle, &layer); - break; default: result = BAD_VALUE; break; @@ -4721,73 +4638,33 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHa return result; } - bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess(); - wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer); - if (parentHandle != nullptr && parent == nullptr) { - ALOGE("Invalid parent handle %p.", parentHandle.get()); - addToRoot = false; - } - if (parentLayer != nullptr) { - addToRoot = false; + args.addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess(); + // We can safely promote the parent layer in binder thread because we have a strong reference + // to the layer's handle inside this scope. + sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote()); + if (args.parentHandle != nullptr && parent == nullptr) { + ALOGE("Invalid parent handle %p", args.parentHandle.promote().get()); + args.addToRoot = false; } - int parentId = -1; - // We can safely promote the layer in binder thread because we have a strong reference - // to the layer's handle inside this scope or we were passed in a sp reference to the layer. - sp<Layer> parentSp = parent.promote(); - if (parentSp != nullptr) { - parentId = parentSp->getSequence(); - } + const int parentId = parent ? parent->getSequence() : -1; if (mTransactionTracing) { - mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name, - args.flags, parentId); + mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence, + args.name, args.flags, parentId); } - result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint); + uint32_t outTransformHint; + result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint); if (result != NO_ERROR) { return result; } - *outLayerId = layer->sequence; + outResult.transformHint = static_cast<int32_t>(outTransformHint); + outResult.layerId = layer->sequence; + outResult.layerName = String16(layer->getDebugName()); return result; } -status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format, - sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, - sp<Layer>* outLayer) { - // initialize the surfaces - switch (format) { - case PIXEL_FORMAT_TRANSPARENT: - case PIXEL_FORMAT_TRANSLUCENT: - format = PIXEL_FORMAT_RGBA_8888; - break; - case PIXEL_FORMAT_OPAQUE: - format = PIXEL_FORMAT_RGBX_8888; - break; - } - - sp<BufferQueueLayer> layer; - args.textureName = getNewTexture(); - { - // Grab the SF state lock during this since it's the only safe way to access - // RenderEngine when creating a BufferLayerConsumer - // TODO: Check if this lock is still needed here - Mutex::Autolock lock(mStateLock); - layer = getFactory().createBufferQueueLayer(args); - } - - status_t err = layer->setDefaultBufferProperties(0, 0, format); - if (err == NO_ERROR) { - *handle = layer->getHandle(); - *gbp = layer->getProducer(); - *outLayer = layer; - } - - ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err)); - return err; -} - status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle, sp<Layer>* outLayer) { args.textureName = getNewTexture(); @@ -4803,20 +4680,13 @@ status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBi return NO_ERROR; } -status_t SurfaceFlinger::createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* handle, - sp<Layer>* outLayer) { - *outLayer = getFactory().createContainerLayer(args); - *handle = (*outLayer)->getHandle(); - return NO_ERROR; -} - void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) { mLayersPendingRemoval.add(layer); mLayersRemoved = true; setTransactionFlags(eTransactionNeeded); } -void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) { +void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t /* layerId */) { Mutex::Autolock lock(mStateLock); markLayerPendingRemovalLocked(layer); mBufferCountTracker.remove(handle); @@ -4826,8 +4696,6 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) { } } -// --------------------------------------------------------------------------- - void SurfaceFlinger::onInitializeDisplays() { const auto display = getDefaultDisplayDeviceLocked(); if (!display) return; @@ -4836,7 +4704,7 @@ void SurfaceFlinger::onInitializeDisplays() { LOG_ALWAYS_FATAL_IF(token == nullptr); // reset screen orientation and use primary layer stack - Vector<ComposerState> state; + std::vector<ResolvedComposerState> state; Vector<DisplayState> displays; DisplayState d; d.what = DisplayState::eDisplayProjectionChanged | @@ -4859,19 +4727,13 @@ void SurfaceFlinger::onInitializeDisplays() { {}, mPid, getuid(), transactionId); setPowerModeInternal(display, hal::PowerMode::ON); - const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod(); - mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod); - mActiveDisplayTransformHint = display->getTransformHint(); - // 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); } void SurfaceFlinger::initializeDisplays() { // Async since we may be called from the main thread. - static_cast<void>( - mScheduler->schedule([this]() FTL_FAKE_GUARD(mStateLock) { onInitializeDisplays(); })); + static_cast<void>(mScheduler->schedule( + [this]() FTL_FAKE_GUARD(mStateLock) + FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); })); } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { @@ -4883,28 +4745,45 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: const auto displayId = display->getPhysicalId(); ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); - std::optional<hal::PowerMode> currentMode = display->getPowerMode(); - if (currentMode.has_value() && mode == *currentMode) { + const auto currentModeOpt = display->getPowerMode(); + if (currentModeOpt == mode) { return; } - const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken); - if (activeDisplay != display && display->isInternal() && activeDisplay && - activeDisplay->isPoweredOn()) { - ALOGW("Trying to change power mode on non active display while the active display is ON"); - } + const bool isActiveDisplay = displayId == mActiveDisplayId; + const bool isInternalDisplay = mPhysicalDisplays.get(displayId) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + + const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId); + + ALOGW_IF(display != activeDisplay && isInternalDisplay && activeDisplay && + activeDisplay->isPoweredOn(), + "Trying to change power mode on inactive display without powering off active display"); display->setPowerMode(mode); - if (mInterceptor->isEnabled()) { - mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode)); - } - const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps(); - if (*currentMode == hal::PowerMode::OFF) { + const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps(); + if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { // Turn on the display - if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) { - onActiveDisplayChangedLocked(display); + + // Activate the display (which involves a modeset to the active mode): + // 1) When the first (a.k.a. primary) display is powered on during boot. + // 2) When the inner or outer display of a foldable is powered on. This condition relies + // on the above DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the + // same display, then the `activeDisplay->isPoweredOn()` below is true, such that the + // display is not activated every time it is powered on. + // + // TODO(b/255635821): Remove the concept of active display. + const bool activeDisplayChanged = + isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn()); + + static bool sPrimaryDisplay = true; + if (sPrimaryDisplay || activeDisplayChanged) { + onActiveDisplayChangedLocked(activeDisplay, display); + sPrimaryDisplay = false; } + // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315. // We can merge the syscall later. if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) { @@ -4914,14 +4793,13 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno)); } getHwComposer().setPowerMode(displayId, mode); - if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) { + if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) { setHWCVsyncEnabled(displayId, mHWCVsyncPendingState); mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, refreshRate); } mVisibleRegionsDirty = true; - mHasPoweredOff = true; scheduleComposite(FrameHint::kActive); } else if (mode == hal::PowerMode::OFF) { // Turn off the display @@ -4931,7 +4809,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) { ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno)); } - if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) { + if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } @@ -4945,13 +4823,16 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) { + if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) { + ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); + mVisibleRegionsDirty = true; + scheduleRepaint(); mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, refreshRate); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze - if (isDisplayActiveLocked(display)) { + if (isActiveDisplay) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } @@ -4961,7 +4842,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: getHwComposer().setPowerMode(displayId, mode); } - if (isDisplayActiveLocked(display)) { + if (isActiveDisplay) { mTimeStats->setPowerMode(mode); mRefreshRateStats->setPowerMode(mode); mScheduler->setDisplayPowerMode(mode); @@ -4971,7 +4852,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( + kMainThreadContext) { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { ALOGE("Attempt to set power mode %d for invalid display token %p", mode, @@ -5002,17 +4884,18 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)}, {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)}, - {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })}, {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, + {"--events"s, dumper(&SurfaceFlinger::dumpEvents)}, + {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, + {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)}, - {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)}, + {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)}, {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, - {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)}, + {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)}, {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)}, - {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, }; const auto flag = args.empty() ? ""s : std::string(String8(args[0])); @@ -5075,13 +4958,6 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { - if (asProto) { - mLayerTracing.writeToFile(); - if (mTransactionTracing) { - mTransactionTracing->writeToFile(); - } - } - return doDump(fd, DumpArgs(), asProto); } @@ -5092,17 +4968,14 @@ void SurfaceFlinger::listLayersLocked(std::string& result) const { void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); + if (args.size() < 2) return; - if (args.size() > 1) { - const auto name = String8(args[1]); - mCurrentState.traverseInZOrder([&](Layer* layer) { - if (layer->getName() == name.string()) { - layer->dumpFrameStats(result); - } - }); - } else { - mAnimFrameTracker.dumpStats(result); - } + const auto name = String8(args[1]); + mCurrentState.traverseInZOrder([&](Layer* layer) { + if (layer->getName() == name.string()) { + layer->dumpFrameStats(result); + } + }); } void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { @@ -5114,8 +4987,6 @@ void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { layer->clearFrameStats(); } }); - - mAnimFrameTracker.clearStats(); } void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const { @@ -5126,12 +4997,13 @@ void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result mFrameTimeline->parseArgs(args, result); } -void SurfaceFlinger::logFrameStats() { - mDrawingState.traverse([&](Layer* layer) { - layer->logFrameStats(); - }); +void SurfaceFlinger::logFrameStats(TimePoint now) { + static TimePoint sTimestamp = now; + if (now - sTimestamp < 30min) return; + sTimestamp = now; - mAnimFrameTracker.logAndResetStats("<win-anim>"); + ATRACE_CALL(); + mDrawingState.traverse([&](Layer* layer) { layer->logFrameStats(); }); } void SurfaceFlinger::appendSfConfigString(std::string& result) const { @@ -5147,24 +5019,39 @@ void SurfaceFlinger::appendSfConfigString(std::string& result) const { result.append("]"); } -void SurfaceFlinger::dumpVSync(std::string& result) const { - mScheduler->dump(result); +void SurfaceFlinger::dumpScheduler(std::string& result) const { + utils::Dumper dumper{result}; + + mScheduler->dump(dumper); + + // TODO(b/241286146): Move to Scheduler. + { + utils::Dumper::Indent indent(dumper); + dumper.dump("lastHwcVsyncState"sv, mLastHWCVsyncState); + dumper.dump("pendingHwcVsyncState"sv, mHWCVsyncPendingState); + } + dumper.eol(); + + // TODO(b/241285876): Move to DisplayModeController. + dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor); + dumper.eol(); mRefreshRateStats->dump(result); - result.append("\n"); + dumper.eol(); mVsyncConfiguration->dump(result); StringAppendF(&result, - " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", + " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 + " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); +} - StringAppendF(&result, "(mode override by backdoor: %s)\n\n", - mDebugDisplayModeSetByBackdoor ? "yes" : "no"); - +void SurfaceFlinger::dumpEvents(std::string& result) const { mScheduler->dump(mAppConnectionHandle, result); +} + +void SurfaceFlinger::dumpVsync(std::string& result) const { mScheduler->dumpVsync(result); - StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n", - to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str()); } void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const { @@ -5174,21 +5061,6 @@ void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) } } -void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { - result.append("Static screen stats:\n"); - for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) { - float bucketTimeSec = getBE().mFrameBuckets[b] / 1e9; - float percent = 100.0f * - static_cast<float>(getBE().mFrameBuckets[b]) / getBE().mTotalTime; - StringAppendF(&result, " < %zd frames: %.3f s (%.1f%%)\n", b + 1, bucketTimeSec, percent); - } - float bucketTimeSec = getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] / 1e9; - float percent = 100.0f * - static_cast<float>(getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1]) / getBE().mTotalTime; - StringAppendF(&result, " %zd+ frames: %.3f s (%.1f%%)\n", SurfaceFlingerBE::NUM_BUCKETS - 1, - bucketTimeSec, percent); -} - void SurfaceFlinger::dumpCompositionDisplays(std::string& result) const { for (const auto& [token, display] : mDisplays) { display->getCompositionDisplay()->dump(result); @@ -5197,9 +5069,25 @@ void SurfaceFlinger::dumpCompositionDisplays(std::string& result) const { } void SurfaceFlinger::dumpDisplays(std::string& result) const { + utils::Dumper dumper{result}; + + for (const auto& [id, display] : mPhysicalDisplays) { + utils::Dumper::Section section(dumper, ftl::Concat("Display ", id.value).str()); + + display.snapshot().dump(dumper); + + if (const auto device = getDisplayDeviceLocked(id)) { + device->dump(dumper); + } + } + for (const auto& [token, display] : mDisplays) { - display->dump(result); - result += '\n'; + if (display->isVirtual()) { + const auto displayId = display->getId(); + utils::Dumper::Section section(dumper, + ftl::Concat("Virtual Display ", displayId.value).str()); + display->dump(dumper); + } } } @@ -5254,35 +5142,45 @@ void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args, } void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { - StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay); + StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor); StringAppendF(&result, "Device uses color management: %d\n", useColorManagement); StringAppendF(&result, "DisplayColorSetting: %s\n", decodeDisplayColorSetting(mDisplayColorSetting).c_str()); // TODO: print out if wide-color mode is active or not - for (const auto& [token, display] : mDisplays) { - const auto displayId = PhysicalDisplayId::tryCast(display->getId()); - if (!displayId) { - continue; - } - - StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str()); - std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId); - for (auto&& mode : modes) { + for (const auto& [id, display] : mPhysicalDisplays) { + StringAppendF(&result, "Display %s color modes:\n", to_string(id).c_str()); + for (const auto mode : display.snapshot().colorModes()) { StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(), mode); } - ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode; - StringAppendF(&result, " Current color mode: %s (%d)\n", - decodeColorMode(currentMode).c_str(), currentMode); + if (const auto display = getDisplayDeviceLocked(id)) { + ui::ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode; + StringAppendF(&result, " Current color mode: %s (%d)\n", + decodeColorMode(currentMode).c_str(), currentMode); + } } result.append("\n"); } LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { + std::unordered_set<uint64_t> stackIdsToSkip; + + // Determine if virtual layers display should be skipped + if ((traceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) { + for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { + if (display->isVirtual()) { + stackIdsToSkip.insert(display->getLayerStack().id); + } + } + } + LayersProto layersProto; for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) { + if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) { + continue; + } layer->writeToProto(layersProto, traceFlags); } @@ -5338,7 +5236,7 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { std::string result; for (Layer* offscreenLayer : mOffscreenLayers) { offscreenLayer->traverse(LayerVector::StateSet::Drawing, - [&](Layer* layer) { layer->dumpCallingUidPid(result); }); + [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); }); } return result; }); @@ -5347,6 +5245,23 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { result.append(future.get()); } +void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const { + for (const auto& [token, display] : mDisplays) { + const auto displayId = HalDisplayId::tryCast(display->getId()); + if (!displayId) { + continue; + } + + StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(), + displayId == mActiveDisplayId ? "active" : "inactive"); + Layer::miniDumpHeader(result); + + const DisplayDevice& ref = *display; + mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); }); + result.append("\n"); + } +} + void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, std::string& result) const { const bool colorize = !args.empty() && args[0] == String16("--color"); @@ -5382,10 +5297,9 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp colorizer.bold(result); result.append("Scheduler:\n"); colorizer.reset(result); - dumpVSync(result); - result.append("\n"); - - dumpStaticScreenStats(result); + dumpScheduler(result); + dumpEvents(result); + dumpVsync(result); result.append("\n"); StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load()); @@ -5430,17 +5344,15 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp StringAppendF(&result, " orientation=%s, isPoweredOn=%d\n", toCString(display->getOrientation()), display->isPoweredOn()); } - StringAppendF(&result, - " transaction-flags : %08x\n" - " gpu_to_cpu_unsupported : %d\n", - mTransactionFlags.load(), !mGpuToCpuSupported); + StringAppendF(&result, " transaction-flags : %08x\n", mTransactionFlags.load()); if (const auto display = getDefaultDisplayDeviceLocked()) { std::string fps, xDpi, yDpi; - if (const auto activeMode = display->getActiveMode()) { - fps = to_string(activeMode->getFps()); + if (const auto activeModePtr = + display->refreshRateSelector().getActiveMode().modePtr.get()) { + fps = to_string(activeModePtr->getFps()); - const auto dpi = activeMode->getDpi(); + const auto dpi = activeModePtr->getDpi(); xDpi = base::StringPrintf("%.2f", dpi.x); yDpi = base::StringPrintf("%.2f", dpi.y); } else { @@ -5471,23 +5383,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp } result.push_back('\n'); - /* - * HWC layer minidump - */ - for (const auto& [token, display] : mDisplays) { - const auto displayId = HalDisplayId::tryCast(display->getId()); - if (!displayId) { - continue; - } - - StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(), - (isDisplayActiveLocked(display) ? "active" : "inactive")); - Layer::miniDumpHeader(result); - - const DisplayDevice& ref = *display; - mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); }); - result.append("\n"); - } + dumpHwcLayersMinidumpLocked(result); { DumpArgs plannerArgs; @@ -5550,29 +5446,11 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { #pragma clang diagnostic push #pragma clang diagnostic error "-Wswitch-enum" switch (static_cast<ISurfaceComposerTag>(code)) { - case ENABLE_VSYNC_INJECTIONS: - case INJECT_VSYNC: - if (!hasMockHwc()) return PERMISSION_DENIED; - [[fallthrough]]; // These methods should at minimum make sure that the client requested // access to SF. - case BOOT_FINISHED: - case CLEAR_ANIMATION_FRAME_STATS: - case GET_ANIMATION_FRAME_STATS: - case OVERRIDE_HDR_TYPES: case GET_HDR_CAPABILITIES: - case SET_DESIRED_DISPLAY_MODE_SPECS: - case GET_DESIRED_DISPLAY_MODE_SPECS: - case SET_ACTIVE_COLOR_MODE: - case SET_BOOT_DISPLAY_MODE: case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: case GET_GAME_CONTENT_TYPE_SUPPORT: - case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: - case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: - case GET_DISPLAYED_CONTENT_SAMPLE: - case ADD_TUNNEL_MODE_ENABLED_LISTENER: - case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: - case SET_GLOBAL_SHADOW_SETTINGS: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary // permission dynamically. Don't use the permission cache for this check. @@ -5585,100 +5463,38 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } return OK; } - case GET_LAYER_DEBUG_INFO: { - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { - ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - return OK; - } - // Used by apps to hook Choreographer to SurfaceFlinger. - case CREATE_DISPLAY_EVENT_CONNECTION: // The following calls are currently used by clients that do not // 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_DISPLAY_MODE: case GET_DISPLAY_COLOR_MODES: - case GET_DISPLAY_NATIVE_PRIMARIES: - case GET_STATIC_DISPLAY_INFO: - case GET_DYNAMIC_DISPLAY_INFO: case GET_DISPLAY_MODES: - case GET_SUPPORTED_FRAME_TIMESTAMPS: // Calling setTransactionState is safe, because you need to have been // granted a reference to Client* and Handle* to do anything with it. - case SET_TRANSACTION_STATE: - case CREATE_CONNECTION: - case GET_COLOR_MANAGEMENT: - case GET_COMPOSITION_PREFERENCE: - case GET_PROTECTED_CONTENT_SUPPORT: - // setFrameRate() is deliberately available for apps to call without any - // special permissions. - case SET_FRAME_RATE: - case GET_DISPLAY_DECORATION_SUPPORT: - case SET_FRAME_TIMELINE_INFO: - case GET_GPU_CONTEXT_PRIORITY: - case GET_MAX_ACQUIRED_BUFFER_COUNT: { + case SET_TRANSACTION_STATE: { // This is not sensitive information, so should not require permission control. return OK; } - case ADD_FPS_LISTENER: - case REMOVE_FPS_LISTENER: - case ADD_REGION_SAMPLING_LISTENER: - case REMOVE_REGION_SAMPLING_LISTENER: { - // codes that require permission check - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - if ((uid != AID_GRAPHICS) && - !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { - ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - return OK; - } - case ADD_TRANSACTION_TRACE_LISTENER: { - IPCThreadState* ipc = IPCThreadState::self(); - const int uid = ipc->getCallingUid(); - if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) { - return OK; - } - return PERMISSION_DENIED; - } - case SET_OVERRIDE_FRAME_RATE: { - const int uid = IPCThreadState::self()->getCallingUid(); - if (uid == AID_ROOT || uid == AID_SYSTEM) { - return OK; - } - return PERMISSION_DENIED; - } - case ON_PULL_ATOM: { - const int uid = IPCThreadState::self()->getCallingUid(); - if (uid == AID_SYSTEM) { - return OK; - } - return PERMISSION_DENIED; - } - case ADD_WINDOW_INFOS_LISTENER: - case REMOVE_WINDOW_INFOS_LISTENER: { - const int uid = IPCThreadState::self()->getCallingUid(); - if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { - return OK; - } - return PERMISSION_DENIED; - } + case BOOT_FINISHED: + // Used by apps to hook Choreographer to SurfaceFlinger. + case CREATE_DISPLAY_EVENT_CONNECTION: + case CREATE_CONNECTION: case CREATE_DISPLAY: case DESTROY_DISPLAY: case GET_PRIMARY_PHYSICAL_DISPLAY_ID: case GET_PHYSICAL_DISPLAY_IDS: case GET_PHYSICAL_DISPLAY_TOKEN: + case AUTHENTICATE_SURFACE: case SET_POWER_MODE: + case GET_SUPPORTED_FRAME_TIMESTAMPS: case GET_DISPLAY_STATE: case GET_DISPLAY_STATS: + case GET_STATIC_DISPLAY_INFO: + case GET_DYNAMIC_DISPLAY_INFO: + case GET_DISPLAY_NATIVE_PRIMARIES: + case SET_ACTIVE_COLOR_MODE: + case SET_BOOT_DISPLAY_MODE: case CLEAR_BOOT_DISPLAY_MODE: case GET_BOOT_DISPLAY_MODE_SUPPORT: case SET_AUTO_LOW_LATENCY_MODE: @@ -5686,12 +5502,43 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case CAPTURE_LAYERS: case CAPTURE_DISPLAY: case CAPTURE_DISPLAY_BY_ID: + case CLEAR_ANIMATION_FRAME_STATS: + case GET_ANIMATION_FRAME_STATS: + case OVERRIDE_HDR_TYPES: + case ON_PULL_ATOM: + case ENABLE_VSYNC_INJECTIONS: + case INJECT_VSYNC: + case GET_LAYER_DEBUG_INFO: + case GET_COLOR_MANAGEMENT: + case GET_COMPOSITION_PREFERENCE: + case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: + case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: + case GET_DISPLAYED_CONTENT_SAMPLE: + case GET_PROTECTED_CONTENT_SUPPORT: case IS_WIDE_COLOR_DISPLAY: + case ADD_REGION_SAMPLING_LISTENER: + case REMOVE_REGION_SAMPLING_LISTENER: + case ADD_FPS_LISTENER: + case REMOVE_FPS_LISTENER: + case ADD_TUNNEL_MODE_ENABLED_LISTENER: + case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: + case ADD_WINDOW_INFOS_LISTENER: + case REMOVE_WINDOW_INFOS_LISTENER: + case SET_DESIRED_DISPLAY_MODE_SPECS: + case GET_DESIRED_DISPLAY_MODE_SPECS: case GET_DISPLAY_BRIGHTNESS_SUPPORT: case SET_DISPLAY_BRIGHTNESS: case ADD_HDR_LAYER_INFO_LISTENER: case REMOVE_HDR_LAYER_INFO_LISTENER: case NOTIFY_POWER_BOOST: + case SET_GLOBAL_SHADOW_SETTINGS: + case GET_DISPLAY_DECORATION_SUPPORT: + case SET_FRAME_RATE: + case SET_OVERRIDE_FRAME_RATE: + case SET_FRAME_TIMELINE_INFO: + case ADD_TRANSACTION_TRACE_LISTENER: + case GET_GPU_CONTEXT_PRIORITY: + case GET_MAX_ACQUIRED_BUFFER_COUNT: LOG_FATAL("Deprecated opcode: %d, migrated to AIDL", code); return PERMISSION_DENIED; } @@ -5773,15 +5620,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r reply->writeInt32(0); reply->writeInt32(mDebugDisableHWC); return NO_ERROR; - case 1013: { - const auto display = getDefaultDisplayDevice(); - if (!display) { - return NAME_NOT_FOUND; - } - - reply->writeInt32(display->getPageFlipCount()); - return NO_ERROR; - } + case 1013: // Unused. + return NAME_NOT_FOUND; case 1014: { Mutex::Autolock _l(mStateLock); // daltonize @@ -5852,17 +5692,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } - case 1020: { // Layer updates interceptor - n = data.readInt32(); - if (n) { - ALOGV("Interceptor enabled"); - mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays); - } - else{ - ALOGV("Interceptor disabled"); - mInterceptor->disable(); - } - return NO_ERROR; + case 1020: { // Unused + return NAME_NOT_FOUND; } case 1021: { // Disable HWC virtual displays const bool enable = data.readInt32() != 0; @@ -5877,12 +5708,11 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r updateColorMatrixLocked(); return NO_ERROR; } - case 1023: { // Set native mode - int32_t colorMode; - + case 1023: { // Set color mode. mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32()); - if (data.readInt32(&colorMode) == NO_ERROR) { - mForceColorMode = static_cast<ColorMode>(colorMode); + + if (int32_t colorMode; data.readInt32(&colorMode) == NO_ERROR) { + mForceColorMode = static_cast<ui::ColorMode>(colorMode); } scheduleRepaint(); return NO_ERROR; @@ -5903,7 +5733,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r (fixedStartingTime) ? fixedStartingTime : systemTime(); mScheduler ->schedule([&]() FTL_FAKE_GUARD(mStateLock) { - mLayerTracing.notify("start", startingTime); + mLayerTracing.notify(true /* visibleRegionDirty */, + startingTime, mLastCommittedVsyncId.value); }) .wait(); } @@ -6011,19 +5842,17 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1034: { - auto future = mScheduler->schedule([&] { - switch (n = data.readInt32()) { - case 0: - case 1: - FTL_FAKE_GUARD(mStateLock, - enableRefreshRateOverlay(static_cast<bool>(n))); - break; - default: { - reply->writeBool( - FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled())); - } - } - }); + auto future = mScheduler->schedule( + [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { + switch (n = data.readInt32()) { + case 0: + case 1: + enableRefreshRateOverlay(static_cast<bool>(n)); + break; + default: + reply->writeBool(isRefreshRateOverlayEnabled()); + } + }); future.wait(); return NO_ERROR; @@ -6046,7 +5875,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r }(); mDebugDisplayModeSetByBackdoor = false; - const status_t result = setActiveModeFromBackdoor(display, modeId); + const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId}); mDebugDisplayModeSetByBackdoor = result == NO_ERROR; return result; } @@ -6056,7 +5885,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1036: { if (data.readInt32() > 0) { // turn on return mScheduler - ->schedule([this] { + ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); @@ -6066,24 +5895,22 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r // defaultMode. The defaultMode doesn't matter for the override // policy though, since we set allowGroupSwitching to true, so it's // not a problem. - scheduler::RefreshRateConfigs::Policy overridePolicy; - overridePolicy.defaultMode = display->refreshRateConfigs() + scheduler::RefreshRateSelector::OverridePolicy overridePolicy; + overridePolicy.defaultMode = display->refreshRateSelector() .getDisplayManagerPolicy() .defaultMode; overridePolicy.allowGroupSwitching = true; - constexpr bool kOverridePolicy = true; - return setDesiredDisplayModeSpecsInternal(display, overridePolicy, - kOverridePolicy); + return setDesiredDisplayModeSpecsInternal(display, overridePolicy); }) .get(); } else { // turn off return mScheduler - ->schedule([this] { + ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); - constexpr bool kOverridePolicy = true; - return setDesiredDisplayModeSpecsInternal(display, {}, - kOverridePolicy); + return setDesiredDisplayModeSpecsInternal( + display, + scheduler::RefreshRateSelector::NoOverridePolicy{}); }) .get(); } @@ -6194,7 +6021,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { if (!updateOverlay) return; // Update the overlay on the main thread to avoid race conditions with - // mRefreshRateConfigs->getActiveMode() + // RefreshRateSelector::getActiveMode static_cast<void>(mScheduler->schedule([=] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { @@ -6205,7 +6032,8 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { const auto desiredActiveMode = display->getDesiredActiveMode(); const std::optional<DisplayModeId> desiredModeId = desiredActiveMode - ? std::make_optional(desiredActiveMode->mode->getId()) + ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId()) + : std::nullopt; const bool timerExpired = mKernelIdleTimerEnabled && expired; @@ -6258,7 +6086,7 @@ void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout, } void SurfaceFlinger::toggleKernelIdleTimer() { - using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; + using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction; const auto display = getDefaultDisplayDeviceLocked(); if (!display) { @@ -6269,12 +6097,12 @@ void SurfaceFlinger::toggleKernelIdleTimer() { // If the support for kernel idle timer is disabled for the active display, // don't do anything. const std::optional<KernelIdleTimerController> kernelIdleTimerController = - display->refreshRateConfigs().kernelIdleTimerController(); + display->refreshRateSelector().kernelIdleTimerController(); if (!kernelIdleTimerController.has_value()) { return; } - const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction(); + const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction(); switch (action) { case KernelIdleTimerAction::TurnOff: @@ -6290,7 +6118,7 @@ void SurfaceFlinger::toggleKernelIdleTimer() { if (!mKernelIdleTimerEnabled) { ATRACE_INT("KernelIdleTimer", 1); const std::chrono::milliseconds timeout = - display->refreshRateConfigs().getIdleTimerTimeout(); + display->refreshRateSelector().getIdleTimerTimeout(); updateKernelIdleTimer(timeout, kernelIdleTimerController.value(), display->getPhysicalId()); mKernelIdleTimerEnabled = true; @@ -6312,18 +6140,6 @@ private: const int mApi; }; -static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) { - switch (colorMode) { - case ColorMode::DISPLAY_P3: - case ColorMode::BT2100_PQ: - case ColorMode::BT2100_HLG: - case ColorMode::DISPLAY_BT2020: - return Dataspace::DISPLAY_P3; - default: - return Dataspace::V0_SRGB; - } -} - static bool hasCaptureBlackoutContentPermission() { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); @@ -6435,15 +6251,11 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, reqSize = display->getLayerStackSpaceRect().getSize(); } - // The dataspace is depended on the color mode of display, that could use non-native mode - // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes, - // and failed if display is not in native mode. This provide a way to force using native - // colors when capture. - dataspace = args.dataspace; - if (dataspace == ui::Dataspace::UNKNOWN) { - const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; - dataspace = pickDataspaceFromColorMode(colorMode); - } + // Allow the caller to specify a dataspace regardless of the display's color mode, e.g. if + // it wants sRGB regardless of the display's wide color mode. + dataspace = args.dataspace == ui::Dataspace::UNKNOWN + ? ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode) + : args.dataspace; } RenderAreaFuture renderAreaFuture = ftl::defer([=] { @@ -6478,9 +6290,7 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId, displayWeak = display; layerStack = display->getLayerStack(); size = display->getLayerStackSpaceRect().getSize(); - - dataspace = - pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode); + dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode); } RenderAreaFuture renderAreaFuture = ftl::defer([=] { @@ -6528,7 +6338,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, { Mutex::Autolock lock(mStateLock); - parent = fromHandle(args.layerHandle).promote(); + parent = LayerHandle::getLayer(args.layerHandle); if (parent == nullptr) { ALOGE("captureLayers called with an invalid or removed parent"); return NAME_NOT_FOUND; @@ -6559,7 +6369,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); for (const auto& handle : args.excludeHandles) { - sp<Layer> excludeLayer = fromHandle(handle).promote(); + sp<Layer> excludeLayer = LayerHandle::getLayer(handle); if (excludeLayer != nullptr) { excludeLayers.emplace(excludeLayer); } else { @@ -6581,7 +6391,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, return BAD_VALUE; } - Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height); + Rect layerStackSpaceRect(crop.left, crop.top, crop.left + reqSize.width, + crop.top + reqSize.height); bool childrenOnly = args.childrenOnly; RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace, @@ -6599,7 +6410,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, return; } - sp<Layer> p = layer; + auto p = sp<Layer>::fromExisting(layer); while (p != nullptr) { if (excludeLayers.count(p) != 0) { return; @@ -6687,44 +6498,40 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission(); - auto future = mScheduler->schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable - -> ftl::SharedFuture<FenceResult> { - ScreenCaptureResults captureResults; - std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); - if (!renderArea) { - ALOGW("Skipping screen capture because of invalid render area."); - if (captureListener) { - captureResults.result = NO_MEMORY; - captureListener->onScreenCaptureCompleted(captureResults); - } - return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); - } + auto future = mScheduler->schedule( + [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( + kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { + ScreenCaptureResults captureResults; + std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); + if (!renderArea) { + ALOGW("Skipping screen capture because of invalid render area."); + if (captureListener) { + captureResults.fenceResult = base::unexpected(NO_MEMORY); + captureListener->onScreenCaptureCompleted(captureResults); + } + return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); + } - ftl::SharedFuture<FenceResult> renderFuture; - renderArea->render([&] { - renderFuture = - renderScreenImpl(*renderArea, traverseLayers, buffer, canCaptureBlackoutContent, - regionSampling, grayscale, captureResults); - }); + ftl::SharedFuture<FenceResult> renderFuture; + renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) { + renderFuture = renderScreenImpl(std::move(renderArea), traverseLayers, buffer, + canCaptureBlackoutContent, regionSampling, + grayscale, captureResults); + }); - if (captureListener) { - // TODO: The future returned by std::async blocks the main thread. Return a chain of - // futures to the Binder thread instead. - std::async([=]() mutable { - ATRACE_NAME("captureListener is nonnull!"); - auto fenceResult = renderFuture.get(); - // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult. - captureResults.result = fenceStatus(fenceResult); - captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE); - captureListener->onScreenCaptureCompleted(captureResults); + if (captureListener) { + // Defer blocking on renderFuture back to the Binder thread. + return ftl::Future(std::move(renderFuture)) + .then([captureListener, captureResults = std::move(captureResults)]( + FenceResult fenceResult) mutable -> FenceResult { + captureResults.fenceResult = std::move(fenceResult); + captureListener->onScreenCaptureCompleted(captureResults); + return base::unexpected(NO_ERROR); + }) + .share(); + } + return renderFuture; }); - } - return renderFuture; - }); - - if (captureListener) { - return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); - } // Flatten nested futures. auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) { @@ -6735,19 +6542,19 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - const RenderArea& renderArea, TraverseLayersFunction traverseLayers, + std::unique_ptr<RenderArea> renderArea, TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) { ATRACE_CALL(); + size_t layerCount = 0; traverseLayers([&](Layer* layer) { + layerCount++; captureResults.capturedSecureLayers = captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure()); }); - const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; - // We allow the system server to take screenshots of secure layers for // use in situations like the Screen-rotation animation and place // the impetus on WindowManager to not persist them. @@ -6757,13 +6564,13 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( } captureResults.buffer = buffer->getBuffer(); - auto dataspace = renderArea.getReqDataSpace(); - auto parent = renderArea.getParentLayer(); + auto dataspace = renderArea->getReqDataSpace(); + auto parent = renderArea->getParentLayer(); auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC; auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance; auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance; - if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) { + if (dataspace == ui::Dataspace::UNKNOWN && parent) { Mutex::Autolock lock(mStateLock); auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) { return display.getLayerStack() == layerStack; @@ -6773,129 +6580,113 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( display = getDefaultDisplayDeviceLocked(); } - const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; - dataspace = pickDataspaceFromColorMode(colorMode); + dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode); renderIntent = display->getCompositionDisplay()->getState().renderIntent; sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits; displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits; } captureResults.capturedDataspace = dataspace; - const auto reqWidth = renderArea.getReqWidth(); - const auto reqHeight = renderArea.getReqHeight(); - const auto sourceCrop = renderArea.getSourceCrop(); - const auto transform = renderArea.getTransform(); - const auto rotation = renderArea.getRotationFlags(); - const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect(); - - renderengine::DisplaySettings clientCompositionDisplay; - std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers; - - // assume that bounds are never offset, and that they are the same as the - // buffer bounds. - clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight); - clientCompositionDisplay.clip = sourceCrop; - clientCompositionDisplay.orientation = rotation; - - clientCompositionDisplay.outputDataspace = dataspace; - clientCompositionDisplay.currentLuminanceNits = displayBrightnessNits; - clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance; - clientCompositionDisplay.renderIntent = - static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(renderIntent); - - const float colorSaturation = grayscale ? 0 : 1; - clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation); - - const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill()); - - compositionengine::LayerFE::LayerSettings fillLayer; - fillLayer.source.buffer.buffer = nullptr; - fillLayer.source.solidColor = half3(0.0, 0.0, 0.0); - fillLayer.geometry.boundaries = - FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom); - fillLayer.alpha = half(alpha); - clientCompositionLayers.push_back(fillLayer); - - const auto display = renderArea.getDisplayDevice(); - std::vector<Layer*> renderedLayers; - bool disableBlurs = false; + const auto transform = renderArea->getTransform(); + const auto display = renderArea->getDisplayDevice(); + + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + layers.reserve(layerCount); + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot; traverseLayers([&](Layer* layer) { - disableBlurs |= layer->getDrawingState().sidebandStream != nullptr; - - Region clip(renderArea.getBounds()); - compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ - clip, - layer->needsFilteringForScreenshots(display.get(), transform) || - renderArea.needsFiltering(), - renderArea.isSecure(), - useProtected, - layerStackSpaceRect, - clientCompositionDisplay.outputDataspace, - true, /* realContentIsVisible */ - false, /* clearContent */ - disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings:: - BlurSetting::Disabled - : compositionengine::LayerFE::ClientCompositionTargetSettings:: - BlurSetting::Enabled, - isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits, + captureResults.capturedHdrLayers |= isHdrLayer(layer); + // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting + // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are + // generated with the layer's current buffer and geometry. + layer->updateSnapshot(true /* updateGeometry */); - }; - std::vector<compositionengine::LayerFE::LayerSettings> results = - layer->prepareClientCompositionList(targetSettings); - if (results.size() > 0) { - for (auto& settings : results) { - settings.geometry.positionTransform = - transform.asMatrix4() * settings.geometry.positionTransform; - // There's no need to process blurs when we're executing region sampling, - // we're just trying to understand what we're drawing, and doing so without - // blurs is already a pretty good approximation. - if (regionSampling) { - settings.backgroundBlurRadius = 0; - } - captureResults.capturedHdrLayers |= isHdrLayer(layer); - } + layers.emplace_back(layer, layer->copyCompositionEngineLayerFE()); - clientCompositionLayers.insert(clientCompositionLayers.end(), - std::make_move_iterator(results.begin()), - std::make_move_iterator(results.end())); - renderedLayers.push_back(layer); - } + sp<LayerFE>& layerFE = layers.back().second; + + layerFE->mSnapshot->geomLayerTransform = + renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform; + if (layer->needsFilteringForScreenshots(display.get(), transform)) { + filterForScreenshot.insert(layerFE.get()); + } }); - std::vector<renderengine::LayerSettings> clientRenderEngineLayers; - clientRenderEngineLayers.reserve(clientCompositionLayers.size()); - std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(), - std::back_inserter(clientRenderEngineLayers), - [](compositionengine::LayerFE::LayerSettings& settings) - -> renderengine::LayerSettings { return settings; }); + ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK}; + if (!layers.empty()) { + const sp<LayerFE>& layerFE = layers.back().second; + layerStack = layerFE->getCompositionState()->outputFilter.layerStack; + } - // 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; - getRenderEngine().useProtectedContext(useProtected); + auto copyLayerFEs = [&layers]() { + std::vector<sp<compositionengine::LayerFE>> layerFEs; + layerFEs.reserve(layers.size()); + for (const auto& [_, layerFE] : layers) { + layerFEs.push_back(layerFE); + } + return layerFEs; + }; - constexpr bool kUseFramebufferCache = false; - auto chain = - ftl::Future(getRenderEngine().drawLayers(clientCompositionDisplay, - clientRenderEngineLayers, buffer, - kUseFramebufferCache, std::move(bufferFence))) - .then(&toFenceResult); + auto present = [this, buffer = std::move(buffer), dataspace, sdrWhitePointNits, + displayBrightnessNits, filterForScreenshot = std::move(filterForScreenshot), + grayscale, layerFEs = copyLayerFEs(), layerStack, regionSampling, + renderArea = std::move(renderArea), renderIntent]() -> FenceResult { + std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = + mFactory.createCompositionEngine(); + compositionEngine->setRenderEngine(mRenderEngine.get()); + + compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace, + .renderIntent = renderIntent}; + + std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput( + ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine, + .colorProfile = colorProfile, + .renderArea = *renderArea, + .layerStack = layerStack, + .buffer = std::move(buffer), + .sdrWhitePointNits = sdrWhitePointNits, + .displayBrightnessNits = displayBrightnessNits, + .filterForScreenshot = std::move(filterForScreenshot), + .regionSampling = regionSampling}); + + const float colorSaturation = grayscale ? 0 : 1; + compositionengine::CompositionRefreshArgs refreshArgs{ + .outputs = {output}, + .layers = std::move(layerFEs), + .updatingOutputGeometryThisFrame = true, + .updatingGeometryThisFrame = true, + .colorTransformMatrix = calculateColorMatrix(colorSaturation), + }; + compositionEngine->present(refreshArgs); - const auto future = chain.share(); - for (auto* layer : renderedLayers) { - layer->onLayerDisplayed(future); - } + return output->getRenderSurface()->getClientTargetAcquireFence(); + }; - // Always switch back to unprotected context. - getRenderEngine().useProtectedContext(false); + // If RenderEngine is threaded, we can safely call CompositionEngine::present off the main + // thread as the RenderEngine::drawLayers call will run on RenderEngine's thread. Otherwise, + // we need RenderEngine to run on the main thread so we call CompositionEngine::present + // immediately. + // + // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call + // to CompositionEngine::present. + const bool renderEngineIsThreaded = [&]() { + using Type = renderengine::RenderEngine::RenderEngineType; + const auto type = mRenderEngine->getRenderEngineType(); + return type == Type::THREADED || type == Type::SKIA_GL_THREADED; + }(); + auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share() + : ftl::yield(present()).share(); - return future; -} + for (auto& [layer, layerFE] : layers) { + layer->onLayerDisplayed( + ftl::Future(presentFuture) + .then([layerFE = std::move(layerFE)](FenceResult) { + return layerFE->stealCompositionResult().releaseFences.back().get(); + }) + .share()); + } -void SurfaceFlinger::windowInfosReported() { - Mutex::Autolock _l(mStateLock); - signalSynchronousTransactions(CountDownLatch::eSyncInputWindows); + return presentFuture; } // --------------------------------------------------------------------------- @@ -6936,9 +6727,28 @@ void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const } } +ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode( + PhysicalDisplayId displayId, DisplayModeId defaultModeId) const { + if (const auto schedulerMode = mScheduler->getPreferredDisplayMode(); + schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) { + return schedulerMode; + } + + return mPhysicalDisplays.get(displayId) + .transform(&PhysicalDisplay::snapshotRef) + .and_then([&](const display::DisplaySnapshot& snapshot) { + return snapshot.displayModes().get(defaultModeId); + }) + .transform([](const DisplayModePtr& modePtr) { + return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)}; + }); +} + status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( const sp<DisplayDevice>& display, - const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) { + const scheduler::RefreshRateSelector::PolicyVariant& policy) { + const auto displayId = display->getPhysicalId(); + Mutex::Autolock lock(mStateLock); if (mDebugDisplayModeSetByBackdoor) { @@ -6946,64 +6756,88 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( return NO_ERROR; } - const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy); - if (setPolicyResult < 0) { - return BAD_VALUE; - } - if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) { - return NO_ERROR; - } + auto& selector = display->refreshRateSelector(); + using SetPolicyResult = scheduler::RefreshRateSelector::SetPolicyResult; - scheduler::RefreshRateConfigs::Policy currentPolicy = - display->refreshRateConfigs().getCurrentPolicy(); + switch (selector.setPolicy(policy)) { + case SetPolicyResult::Invalid: + return BAD_VALUE; + case SetPolicyResult::Unchanged: + return NO_ERROR; + case SetPolicyResult::Changed: + return applyRefreshRateSelectorPolicy(displayId, selector); + } +} +status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( + PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) { + const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. - const auto activeMode = display->getActiveMode(); - if (isDisplayActiveLocked(display)) { + if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) { mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); toggleKernelIdleTimer(); } else { mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); } - const DisplayModePtr preferredDisplayMode = [&] { - const auto schedulerMode = mScheduler->getPreferredDisplayMode(); - if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) { - return schedulerMode; - } + auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode); + if (!preferredModeOpt) { + ALOGE("%s: Preferred mode is unknown", __func__); + return NAME_NOT_FOUND; + } - return display->getMode(currentPolicy.defaultMode); - }(); + auto preferredMode = std::move(*preferredModeOpt); + const auto preferredModeId = preferredMode.modePtr->getId(); - ALOGV("trying to switch to Scheduler preferred mode %d (%s)", - preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str()); + ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(), + to_string(preferredMode.fps).c_str()); - if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) { - ALOGV("switching to Scheduler preferred display mode %d", - preferredDisplayMode->getId().value()); - setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed}); - } else { - LOG_ALWAYS_FATAL("Desired display mode not allowed: %d", - preferredDisplayMode->getId().value()); + if (!selector.isModeAllowed(preferredMode)) { + ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value()); + return INVALID_OPERATION; } + setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}); return NO_ERROR; } -status_t SurfaceFlinger::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +namespace { +FpsRange translate(const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& aidlRange) { + return FpsRange{Fps::fromValue(aidlRange.min), Fps::fromValue(aidlRange.max)}; +} + +FpsRanges translate(const gui::DisplayModeSpecs::RefreshRateRanges& aidlRanges) { + return FpsRanges{translate(aidlRanges.physical), translate(aidlRanges.render)}; +} + +gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange translate(const FpsRange& range) { + gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange aidlRange; + aidlRange.min = range.min.getValue(); + aidlRange.max = range.max.getValue(); + return aidlRange; +} + +gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) { + gui::DisplayModeSpecs::RefreshRateRanges aidlRanges; + aidlRanges.physical = translate(ranges.physical); + aidlRanges.render = translate(ranges.render); + return aidlRanges; +} + +} // namespace + +status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { ATRACE_CALL(); if (!displayToken) { return BAD_VALUE; } - auto future = mScheduler->schedule([=]() -> status_t { + auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set desired display modes for invalid display token %p", @@ -7013,16 +6847,11 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs( ALOGW("Attempt to set desired display modes for virtual display"); return INVALID_OPERATION; } else { - using Policy = scheduler::RefreshRateConfigs::Policy; - const Policy policy{DisplayModeId(defaultMode), - allowGroupSwitching, - {Fps::fromValue(primaryRefreshRateMin), - Fps::fromValue(primaryRefreshRateMax)}, - {Fps::fromValue(appRequestRefreshRateMin), - Fps::fromValue(appRequestRefreshRateMax)}}; - constexpr bool kOverridePolicy = false; + using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy; + const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges), + translate(specs.appRequestRanges), specs.allowGroupSwitching}; - return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy); + return setDesiredDisplayModeSpecsInternal(display, policy); } }); @@ -7030,16 +6859,10 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs( } status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { + gui::DisplayModeSpecs* outSpecs) { ATRACE_CALL(); - if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin || - !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) { + if (!displayToken || !outSpecs) { return BAD_VALUE; } @@ -7053,21 +6876,15 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return INVALID_OPERATION; } - scheduler::RefreshRateConfigs::Policy policy = - display->refreshRateConfigs().getDisplayManagerPolicy(); - *outDefaultMode = policy.defaultMode.value(); - *outAllowGroupSwitching = policy.allowGroupSwitching; - *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue(); - *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue(); - *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue(); - *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue(); + scheduler::RefreshRateSelector::Policy policy = + display->refreshRateSelector().getDisplayManagerPolicy(); + outSpecs->defaultMode = policy.defaultMode.value(); + outSpecs->allowGroupSwitching = policy.allowGroupSwitching; + outSpecs->primaryRanges = translate(policy.primaryRanges); + outSpecs->appRequestRanges = translate(policy.appRequestRanges); return NO_ERROR; } -wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const { - return Layer::fromHandle(handle); -} - void SurfaceFlinger::onLayerFirstRef(Layer* layer) { mNumLayers++; if (!layer->isRemovedFromCurrentState()) { @@ -7130,45 +6947,12 @@ const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayer // on the work to remove the table in that bug rather than adding more to // it. static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{ - {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID}, - {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR}, + {"org.chromium.arc.V1_0.TaskId", gui::METADATA_TASK_ID}, + {"org.chromium.arc.V1_0.CursorInfo", gui::METADATA_MOUSE_CURSOR}, }; return genericLayerMetadataKeyMap; } -status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility, int8_t changeFrameRateStrategy) { - if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy, - "SurfaceFlinger::setFrameRate")) { - return BAD_VALUE; - } - - static_cast<void>(mScheduler->schedule([=] { - Mutex::Autolock lock(mStateLock); - if (authenticateSurfaceTextureLocked(surface)) { - sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer(); - if (layer == nullptr) { - ALOGE("Attempt to set frame rate on a layer that no longer exists"); - return BAD_VALUE; - } - const auto strategy = - Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy); - if (layer->setFrameRate( - Layer::FrameRate(Fps::fromValue(frameRate), - Layer::FrameRate::convertCompatibility(compatibility), - strategy))) { - setTransactionFlags(eTraversalNeeded); - } - } else { - ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer"); - return BAD_VALUE; - } - return NO_ERROR; - })); - - return NO_ERROR; -} - status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) { PhysicalDisplayId displayId = [&]() { Mutex::Autolock lock(mStateLock); @@ -7180,44 +6964,18 @@ status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) { return NO_ERROR; } -status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, - const FrameTimelineInfo& frameTimelineInfo) { - Mutex::Autolock lock(mStateLock); - if (!authenticateSurfaceTextureLocked(surface)) { - ALOGE("Attempt to set frame timeline info on an unrecognized IGraphicBufferProducer"); - return BAD_VALUE; - } - - sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer(); - if (layer == nullptr) { - ALOGE("Attempt to set frame timeline info on a layer that no longer exists"); - return BAD_VALUE; - } - - layer->setFrameTimelineInfoForBuffer(frameTimelineInfo); - return NO_ERROR; -} - void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { - for (const auto& [ignored, display] : mDisplays) { - if (display->isInternal()) { - display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner); + for (const auto& [id, display] : mPhysicalDisplays) { + if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { + if (const auto device = getDisplayDeviceLocked(id)) { + device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner, + mRefreshRateOverlayRenderRate); + } } } } -status_t SurfaceFlinger::addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) { - if (!listener) { - return BAD_VALUE; - } - - mInterceptor->addTransactionTraceListener(listener); - - return NO_ERROR; -} - -int SurfaceFlinger::getGPUContextPriority() { +int SurfaceFlinger::getGpuContextPriority() { return getRenderEngine().getContextPriority(); } @@ -7235,7 +6993,7 @@ status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { if (!getHwComposer().isHeadless()) { if (const auto display = getDefaultDisplayDevice()) { - maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max; + maxRefreshRate = display->refreshRateSelector().getSupportedRefreshRateRange().max; } } @@ -7250,7 +7008,7 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui refreshRate = *frameRateOverride; } else if (!getHwComposer().isHeadless()) { if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) { - refreshRate = display->refreshRateConfigs().getActiveMode()->getFps(); + refreshRate = display->refreshRateSelector().getActiveMode().fps; } } @@ -7263,7 +7021,7 @@ int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) con return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } -void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state) { +void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, VsyncId vsyncId) { sp<Layer> layer = state.layer.promote(); if (!layer) { ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get()); @@ -7292,9 +7050,23 @@ void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state) { parent->addChild(layer); } - layer->updateTransformHint(mActiveDisplayTransformHint); + ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current); + sp<const DisplayDevice> hintDisplay; + // Find the display that includes the layer. + for (const auto& [token, display] : mDisplays) { + if (display->getLayerStack() == layerStack) { + hintDisplay = display; + break; + } + } + + if (hintDisplay) { + layer->updateTransformHint(hintDisplay->getTransformHint()); + } - mInterceptor->saveSurfaceCreation(layer); + if (mTransactionTracing) { + mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value); + } } void SurfaceFlinger::sample() { @@ -7305,33 +7077,34 @@ void SurfaceFlinger::sample() { mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime()); } -void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) { +void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<const DisplayDevice>& activeDisplay) { mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight()); getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize()); } -void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) { +void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay, + const sp<DisplayDevice>& activeDisplay) { ATRACE_CALL(); - if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) { - display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false); + if (inactiveDisplay) { + inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false); } - if (!activeDisplay) { - ALOGE("%s: activeDisplay is null", __func__); - return; - } - mActiveDisplayToken = activeDisplay->getDisplayToken(); + mActiveDisplayId = activeDisplay->getPhysicalId(); activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); + updateInternalDisplayVsyncLocked(activeDisplay); mScheduler->setModeChangePending(false); - mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs()); + mScheduler->setLeaderDisplay(mActiveDisplayId); + onActiveDisplaySizeChanged(activeDisplay); mActiveDisplayTransformHint = activeDisplay->getTransformHint(); - // Update the kernel timer for the current active display, since the policy - // for this display might have changed when it was not the active display. - toggleKernelIdleTimer(); + // The policy of the new active/leader display may have changed while it was inactive. In that + // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either + // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode, + // and the kernel idle timer of the newly active display must be toggled. + applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector()); } status_t SurfaceFlinger::addWindowInfosListener( @@ -7347,37 +7120,95 @@ status_t SurfaceFlinger::removeWindowInfosListener( } std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData( - const BufferData& bufferData, const char* layerName) const { - bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged); - bool bufferSizeExceedsLimit = false; - std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; - if (cacheIdChanged && bufferData.buffer != nullptr) { - bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), - bufferData.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer); - buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); - } - } else if (cacheIdChanged) { - buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); - } else if (bufferData.buffer != nullptr) { - bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), - bufferData.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - buffer = std::make_shared< - renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(), - renderengine::impl::ExternalTexture:: - Usage::READABLE); - } - } - ALOGE_IF(bufferSizeExceedsLimit, - "Attempted to create an ExternalTexture for layer %s that exceeds render target size " - "limit.", - layerName); - return buffer; -} - -bool SurfaceFlinger::commitCreatedLayers() { + BufferData& bufferData, const char* layerName, uint64_t transactionId) { + if (bufferData.buffer && + exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), bufferData.buffer->getHeight())) { + std::string errorMessage = + base::StringPrintf("Attempted to create an ExternalTexture with size (%u, %u) for " + "layer %s that exceeds render target size limit of %u.", + bufferData.buffer->getWidth(), bufferData.buffer->getHeight(), + layerName, static_cast<uint32_t>(mMaxRenderTargetSize)); + ALOGD("%s", errorMessage.c_str()); + if (bufferData.releaseBufferListener) { + bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage); + } + return nullptr; + } + + bool cachedBufferChanged = + bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged); + if (cachedBufferChanged && bufferData.buffer) { + auto result = ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer); + if (result.ok()) { + return result.value(); + } + + if (result.error() == ClientCache::AddError::CacheFull) { + ALOGE("Attempted to create an ExternalTexture for layer %s but CacheFull", layerName); + + if (bufferData.releaseBufferListener) { + bufferData.releaseBufferListener->onTransactionQueueStalled( + "Buffer processing hung due to full buffer cache"); + } + } + + return nullptr; + } + + if (cachedBufferChanged) { + return ClientCache::getInstance().get(bufferData.cachedBuffer); + } + + if (bufferData.buffer) { + return std::make_shared< + renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(), + renderengine::impl::ExternalTexture::Usage:: + READABLE); + } + + return nullptr; +} + +bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) { + std::vector<MirrorDisplayState> mirrorDisplays; + { + std::scoped_lock<std::mutex> lock(mMirrorDisplayLock); + mirrorDisplays = std::move(mMirrorDisplays); + mMirrorDisplays.clear(); + if (mirrorDisplays.size() == 0) { + return false; + } + } + + sp<IBinder> unused; + for (const auto& mirrorDisplay : mirrorDisplays) { + // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display + // accidentally. + sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle); + rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1)); + for (const auto& layer : mDrawingState.layersSortedByZ) { + if (layer->getLayerStack() != mirrorDisplay.layerStack || + layer->isInternalDisplayOverlay()) { + continue; + } + + LayerCreationArgs mirrorArgs(this, mirrorDisplay.client, "MirrorLayerParent", + ISurfaceComposerClient::eNoColorFill, + gui::LayerMetadata()); + sp<Layer> childMirror; + createEffectLayer(mirrorArgs, &unused, &childMirror); + childMirror->setClonedChild(layer->createClone()); + if (mTransactionTracing) { + mTransactionTracing->onLayerAddedToDrawingState(childMirror->getSequence(), + vsyncId.value); + } + childMirror->reparent(mirrorDisplay.rootHandle); + } + } + return true; +} + +bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId) { std::vector<LayerCreatedState> createdLayers; { std::scoped_lock<std::mutex> lock(mCreatedLayersLock); @@ -7390,33 +7221,92 @@ bool SurfaceFlinger::commitCreatedLayers() { Mutex::Autolock _l(mStateLock); for (const auto& createdLayer : createdLayers) { - handleLayerCreatedLocked(createdLayer); + handleLayerCreatedLocked(createdLayer, vsyncId); } createdLayers.clear(); mLayersAdded = true; return true; } +void SurfaceFlinger::updateLayerMetadataSnapshot() { + LayerMetadata parentMetadata; + for (const auto& layer : mDrawingState.layersSortedByZ) { + layer->updateMetadataSnapshot(parentMetadata); + } + + std::unordered_set<Layer*> visited; + mDrawingState.traverse([&visited](Layer* layer) { + if (visited.find(layer) != visited.end()) { + return; + } + + // If the layer isRelativeOf, then either it's relative metadata will be set + // recursively when updateRelativeMetadataSnapshot is called on its relative parent or + // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure + // that layers with deleted relative parents don't hold stale relativeLayerMetadata. + if (layer->getDrawingState().isRelativeOf) { + layer->editLayerSnapshot()->relativeLayerMetadata = {}; + return; + } + + layer->updateRelativeMetadataSnapshot({}, visited); + }); +} + // gui::ISurfaceComposer +binder::Status SurfaceComposerAIDL::bootFinished() { + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + mFlinger->bootFinished(); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::createDisplayEventConnection( + VsyncSource vsyncSource, EventRegistration eventRegistration, + sp<IDisplayEventConnection>* outConnection) { + sp<IDisplayEventConnection> conn = + mFlinger->createDisplayEventConnection(vsyncSource, eventRegistration); + if (conn == nullptr) { + *outConnection = nullptr; + return binderStatusFromStatusT(BAD_VALUE); + } else { + *outConnection = conn; + return binder::Status::ok(); + } +} + +binder::Status SurfaceComposerAIDL::createConnection(sp<gui::ISurfaceComposerClient>* outClient) { + const sp<Client> client = sp<Client>::make(mFlinger); + if (client->initCheck() == NO_ERROR) { + *outClient = client; + return binder::Status::ok(); + } else { + *outClient = nullptr; + return binderStatusFromStatusT(BAD_VALUE); + } +} + binder::Status SurfaceComposerAIDL::createDisplay(const std::string& displayName, bool secure, sp<IBinder>* outDisplay) { status_t status = checkAccessPermission(); - if (status == OK) { - String8 displayName8 = String8::format("%s", displayName.c_str()); - *outDisplay = mFlinger->createDisplay(displayName8, secure); - return binder::Status::ok(); + if (status != OK) { + return binderStatusFromStatusT(status); } - return binder::Status::fromStatusT(status); + String8 displayName8 = String8::format("%s", displayName.c_str()); + *outDisplay = mFlinger->createDisplay(displayName8, secure); + return binder::Status::ok(); } binder::Status SurfaceComposerAIDL::destroyDisplay(const sp<IBinder>& display) { status_t status = checkAccessPermission(); - if (status == OK) { - mFlinger->destroyDisplay(display); - return binder::Status::ok(); + if (status != OK) { + return binderStatusFromStatusT(status); } - return binder::Status::fromStatusT(status); + mFlinger->destroyDisplay(display); + return binder::Status::ok(); } binder::Status SurfaceComposerAIDL::getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) { @@ -7430,22 +7320,12 @@ binder::Status SurfaceComposerAIDL::getPhysicalDisplayIds(std::vector<int64_t>* return binder::Status::ok(); } -binder::Status SurfaceComposerAIDL::getPrimaryPhysicalDisplayId(int64_t* outDisplayId) { +binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId, + sp<IBinder>* outDisplay) { status_t status = checkAccessPermission(); if (status != OK) { - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); } - - PhysicalDisplayId id; - status = mFlinger->getPrimaryPhysicalDisplayId(&id); - if (status == NO_ERROR) { - *outDisplayId = id.value; - } - return binder::Status::fromStatusT(status); -} - -binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId, - sp<IBinder>* outDisplay) { const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId)); *outDisplay = mFlinger->getPhysicalDisplayToken(*id); return binder::Status::ok(); @@ -7453,12 +7333,25 @@ binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId, binder::Status SurfaceComposerAIDL::setPowerMode(const sp<IBinder>& display, int mode) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - + if (status != OK) { + return binderStatusFromStatusT(status); + } mFlinger->setPowerMode(display, mode); return binder::Status::ok(); } +binder::Status SurfaceComposerAIDL::getSupportedFrameTimestamps( + std::vector<FrameEvent>* outSupported) { + status_t status; + if (!outSupported) { + status = UNEXPECTED_NULL; + } else { + outSupported->clear(); + status = mFlinger->getSupportedFrameTimestamps(outSupported); + } + return binderStatusFromStatusT(status); +} + binder::Status SurfaceComposerAIDL::getDisplayStats(const sp<IBinder>& display, gui::DisplayStatInfo* outStatInfo) { DisplayStatInfo statInfo; @@ -7467,7 +7360,7 @@ binder::Status SurfaceComposerAIDL::getDisplayStats(const sp<IBinder>& display, outStatInfo->vsyncTime = static_cast<long>(statInfo.vsyncTime); outStatInfo->vsyncPeriod = static_cast<long>(statInfo.vsyncPeriod); } - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::getDisplayState(const sp<IBinder>& display, @@ -7480,37 +7373,201 @@ binder::Status SurfaceComposerAIDL::getDisplayState(const sp<IBinder>& display, outState->layerStackSpaceRect.width = state.layerStackSpaceRect.width; outState->layerStackSpaceRect.height = state.layerStackSpaceRect.height; } - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId, + gui::StaticDisplayInfo* outInfo) { + using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag; + ui::StaticDisplayInfo info; + + status_t status = mFlinger->getStaticDisplayInfo(displayId, &info); + if (status == NO_ERROR) { + // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo + outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType); + outInfo->density = info.density; + outInfo->secure = info.secure; + outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation); + + gui::DeviceProductInfo dinfo; + std::optional<DeviceProductInfo> dpi = info.deviceProductInfo; + dinfo.name = std::move(dpi->name); + dinfo.manufacturerPnpId = + std::vector<uint8_t>(dpi->manufacturerPnpId.begin(), dpi->manufacturerPnpId.end()); + dinfo.productId = dpi->productId; + dinfo.relativeAddress = + std::vector<uint8_t>(dpi->relativeAddress.begin(), dpi->relativeAddress.end()); + if (const auto* model = + std::get_if<DeviceProductInfo::ModelYear>(&dpi->manufactureOrModelDate)) { + gui::DeviceProductInfo::ModelYear modelYear; + modelYear.year = model->year; + dinfo.manufactureOrModelDate.set<Tag::modelYear>(modelYear); + } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureYear>( + &dpi->manufactureOrModelDate)) { + gui::DeviceProductInfo::ManufactureYear date; + date.modelYear.year = manufacture->year; + dinfo.manufactureOrModelDate.set<Tag::manufactureYear>(date); + } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureWeekAndYear>( + &dpi->manufactureOrModelDate)) { + gui::DeviceProductInfo::ManufactureWeekAndYear date; + date.manufactureYear.modelYear.year = manufacture->year; + date.week = manufacture->week; + dinfo.manufactureOrModelDate.set<Tag::manufactureWeekAndYear>(date); + } + + outInfo->deviceProductInfo = dinfo; + } + return binderStatusFromStatusT(status); +} + +void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info, + gui::DynamicDisplayInfo*& outInfo) { + // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo + outInfo->supportedDisplayModes.clear(); + outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size()); + for (const auto& mode : info.supportedDisplayModes) { + gui::DisplayMode outMode; + outMode.id = mode.id; + outMode.resolution.width = mode.resolution.width; + outMode.resolution.height = mode.resolution.height; + outMode.xDpi = mode.xDpi; + outMode.yDpi = mode.yDpi; + outMode.refreshRate = mode.refreshRate; + outMode.appVsyncOffset = mode.appVsyncOffset; + outMode.sfVsyncOffset = mode.sfVsyncOffset; + outMode.presentationDeadline = mode.presentationDeadline; + outMode.group = mode.group; + std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(), + std::back_inserter(outMode.supportedHdrTypes), + [](const ui::Hdr& value) { return static_cast<int32_t>(value); }); + outInfo->supportedDisplayModes.push_back(outMode); + } + + outInfo->activeDisplayModeId = info.activeDisplayModeId; + outInfo->renderFrameRate = info.renderFrameRate; + + outInfo->supportedColorModes.clear(); + outInfo->supportedColorModes.reserve(info.supportedColorModes.size()); + for (const auto& cmode : info.supportedColorModes) { + outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode)); + } + + outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode); + + gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities; + hdrCapabilities.supportedHdrTypes.clear(); + hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size()); + for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) { + hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr)); + } + hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance(); + hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance(); + hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance(); + + outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported; + outInfo->gameContentTypeSupported = info.gameContentTypeSupported; + outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode; +} + +binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken( + const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) { + ui::DynamicDisplayInfo info; + status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info); + if (status == NO_ERROR) { + getDynamicDisplayInfoInternal(info, outInfo); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId, + gui::DynamicDisplayInfo* outInfo) { + ui::DynamicDisplayInfo info; + status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info); + if (status == NO_ERROR) { + getDynamicDisplayInfoInternal(info, outInfo); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDisplayNativePrimaries(const sp<IBinder>& display, + gui::DisplayPrimaries* outPrimaries) { + ui::DisplayPrimaries primaries; + status_t status = mFlinger->getDisplayNativePrimaries(display, primaries); + if (status == NO_ERROR) { + outPrimaries->red.X = primaries.red.X; + outPrimaries->red.Y = primaries.red.Y; + outPrimaries->red.Z = primaries.red.Z; + + outPrimaries->green.X = primaries.green.X; + outPrimaries->green.Y = primaries.green.Y; + outPrimaries->green.Z = primaries.green.Z; + + outPrimaries->blue.X = primaries.blue.X; + outPrimaries->blue.Y = primaries.blue.Y; + outPrimaries->blue.Z = primaries.blue.Z; + + outPrimaries->white.X = primaries.white.X; + outPrimaries->white.Y = primaries.white.Y; + outPrimaries->white.Z = primaries.white.Z; + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setActiveColorMode(const sp<IBinder>& display, int colorMode) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->setActiveColorMode(display, static_cast<ui::ColorMode>(colorMode)); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setBootDisplayMode(const sp<IBinder>& display, + int displayModeId) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->setBootDisplayMode(display, DisplayModeId{displayModeId}); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::clearBootDisplayMode(const sp<IBinder>& display) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->clearBootDisplayMode(display); + } + return binderStatusFromStatusT(status); +} - status = mFlinger->clearBootDisplayMode(display); - return binder::Status::fromStatusT(status); +binder::Status SurfaceComposerAIDL::getOverlaySupport(gui::OverlayProperties* outProperties) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->getOverlaySupport(outProperties); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::getBootDisplayModeSupport(bool* outMode) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - - status = mFlinger->getBootDisplayModeSupport(outMode); - return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->getBootDisplayModeSupport(outMode); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - + if (status != OK) { + return binderStatusFromStatusT(status); + } mFlinger->setAutoLowLatencyMode(display, on); return binder::Status::ok(); } binder::Status SurfaceComposerAIDL::setGameContentType(const sp<IBinder>& display, bool on) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - + if (status != OK) { + return binderStatusFromStatusT(status); + } mFlinger->setGameContentType(display, on); return binder::Status::ok(); } @@ -7518,7 +7575,7 @@ binder::Status SurfaceComposerAIDL::setGameContentType(const sp<IBinder>& displa binder::Status SurfaceComposerAIDL::captureDisplay( const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { status_t status = mFlinger->captureDisplay(args, captureListener); - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::captureDisplayById( @@ -7532,60 +7589,369 @@ binder::Status SurfaceComposerAIDL::captureDisplayById( } else { status = PERMISSION_DENIED; } - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::captureLayers( const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { status_t status = mFlinger->captureLayers(args, captureListener); - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::overrideHdrTypes(const sp<IBinder>& display, + const std::vector<int32_t>& hdrTypes) { + // overrideHdrTypes is used by CTS tests, which acquire the necessary + // permission dynamically. Don't use the permission cache for this check. + status_t status = checkAccessPermission(false); + if (status != OK) { + return binderStatusFromStatusT(status); + } + + std::vector<ui::Hdr> hdrTypesVector; + for (int32_t i : hdrTypes) { + hdrTypesVector.push_back(static_cast<ui::Hdr>(i)); + } + status = mFlinger->overrideHdrTypes(display, hdrTypesVector); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) { + status_t status; + const int uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_SYSTEM) { + status = PERMISSION_DENIED; + } else { + status = mFlinger->onPullAtom(atomId, &outPullData->data, &outPullData->success); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) { + if (!outLayers) { + return binderStatusFromStatusT(UNEXPECTED_NULL); + } + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); + return binderStatusFromStatusT(PERMISSION_DENIED); + } + status_t status = mFlinger->getLayerDebugInfo(outLayers); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getColorManagement(bool* outGetColorManagement) { + status_t status = mFlinger->getColorManagement(outGetColorManagement); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) { + ui::Dataspace dataspace; + ui::PixelFormat pixelFormat; + ui::Dataspace wideColorGamutDataspace; + ui::PixelFormat wideColorGamutPixelFormat; + status_t status = + mFlinger->getCompositionPreference(&dataspace, &pixelFormat, &wideColorGamutDataspace, + &wideColorGamutPixelFormat); + if (status == NO_ERROR) { + outPref->defaultDataspace = static_cast<int32_t>(dataspace); + outPref->defaultPixelFormat = static_cast<int32_t>(pixelFormat); + outPref->wideColorGamutDataspace = static_cast<int32_t>(wideColorGamutDataspace); + outPref->wideColorGamutPixelFormat = static_cast<int32_t>(wideColorGamutPixelFormat); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDisplayedContentSamplingAttributes( + const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) { + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + + ui::PixelFormat format; + ui::Dataspace dataspace; + uint8_t componentMask; + status = mFlinger->getDisplayedContentSamplingAttributes(display, &format, &dataspace, + &componentMask); + if (status == NO_ERROR) { + outAttrs->format = static_cast<int32_t>(format); + outAttrs->dataspace = static_cast<int32_t>(dataspace); + outAttrs->componentMask = static_cast<int8_t>(componentMask); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setDisplayContentSamplingEnabled(const sp<IBinder>& display, + bool enable, + int8_t componentMask, + int64_t maxFrames) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->setDisplayContentSamplingEnabled(display, enable, + static_cast<uint8_t>(componentMask), + static_cast<uint64_t>(maxFrames)); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDisplayedContentSample(const sp<IBinder>& display, + int64_t maxFrames, int64_t timestamp, + gui::DisplayedFrameStats* outStats) { + if (!outStats) { + return binderStatusFromStatusT(BAD_VALUE); + } + + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + + DisplayedFrameStats stats; + status = mFlinger->getDisplayedContentSample(display, static_cast<uint64_t>(maxFrames), + static_cast<uint64_t>(timestamp), &stats); + if (status == NO_ERROR) { + // convert from ui::DisplayedFrameStats to gui::DisplayedFrameStats + outStats->numFrames = static_cast<int64_t>(stats.numFrames); + outStats->component_0_sample.reserve(stats.component_0_sample.size()); + for (const auto& s : stats.component_0_sample) { + outStats->component_0_sample.push_back(static_cast<int64_t>(s)); + } + outStats->component_1_sample.reserve(stats.component_1_sample.size()); + for (const auto& s : stats.component_1_sample) { + outStats->component_1_sample.push_back(static_cast<int64_t>(s)); + } + outStats->component_2_sample.reserve(stats.component_2_sample.size()); + for (const auto& s : stats.component_2_sample) { + outStats->component_2_sample.push_back(static_cast<int64_t>(s)); + } + outStats->component_3_sample.reserve(stats.component_3_sample.size()); + for (const auto& s : stats.component_3_sample) { + outStats->component_3_sample.push_back(static_cast<int64_t>(s)); + } + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getProtectedContentSupport(bool* outSupported) { + status_t status = mFlinger->getProtectedContentSupport(outSupported); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::isWideColorDisplay(const sp<IBinder>& token, bool* outIsWideColorDisplay) { status_t status = mFlinger->isWideColorDisplay(token, outIsWideColorDisplay); - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::addRegionSamplingListener( + const gui::ARect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<gui::IRegionSamplingListener>& listener) { + status_t status = checkReadFrameBufferPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + android::Rect rect; + rect.left = samplingArea.left; + rect.top = samplingArea.top; + rect.right = samplingArea.right; + rect.bottom = samplingArea.bottom; + status = mFlinger->addRegionSamplingListener(rect, stopLayerHandle, listener); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::removeRegionSamplingListener( + const sp<gui::IRegionSamplingListener>& listener) { + status_t status = checkReadFrameBufferPermission(); + if (status == OK) { + status = mFlinger->removeRegionSamplingListener(listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::addFpsListener(int32_t taskId, + const sp<gui::IFpsListener>& listener) { + status_t status = checkReadFrameBufferPermission(); + if (status == OK) { + status = mFlinger->addFpsListener(taskId, listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::removeFpsListener(const sp<gui::IFpsListener>& listener) { + status_t status = checkReadFrameBufferPermission(); + if (status == OK) { + status = mFlinger->removeFpsListener(listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->addTunnelModeEnabledListener(listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->removeTunnelModeEnabledListener(listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->setDesiredDisplayModeSpecs(displayToken, specs); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + gui::DisplayModeSpecs* outSpecs) { + if (!outSpecs) { + return binderStatusFromStatusT(BAD_VALUE); + } + + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + + status = mFlinger->getDesiredDisplayModeSpecs(displayToken, outSpecs); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) { status_t status = mFlinger->getDisplayBrightnessSupport(displayToken, outSupport); - return binder::Status::fromStatusT(status); + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::setDisplayBrightness(const sp<IBinder>& displayToken, const gui::DisplayBrightness& brightness) { status_t status = checkControlDisplayBrightnessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - - status = mFlinger->setDisplayBrightness(displayToken, brightness); - return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->setDisplayBrightness(displayToken, brightness); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::addHdrLayerInfoListener( const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) { status_t status = checkControlDisplayBrightnessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - - status = mFlinger->addHdrLayerInfoListener(displayToken, listener); - return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->addHdrLayerInfoListener(displayToken, listener); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::removeHdrLayerInfoListener( const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) { status_t status = checkControlDisplayBrightnessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); - - status = mFlinger->removeHdrLayerInfoListener(displayToken, listener); - return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->removeHdrLayerInfoListener(displayToken, listener); + } + return binderStatusFromStatusT(status); } binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) { status_t status = checkAccessPermission(); - if (status != OK) return binder::Status::fromStatusT(status); + if (status == OK) { + status = mFlinger->notifyPowerBoost(boostId); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setGlobalShadowSettings(const gui::Color& ambientColor, + const gui::Color& spotColor, + float lightPosY, float lightPosZ, + float lightRadius) { + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + + half4 ambientColorHalf = {ambientColor.r, ambientColor.g, ambientColor.b, ambientColor.a}; + half4 spotColorHalf = {spotColor.r, spotColor.g, spotColor.b, spotColor.a}; + status = mFlinger->setGlobalShadowSettings(ambientColorHalf, spotColorHalf, lightPosY, + lightPosZ, lightRadius); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getDisplayDecorationSupport( + const sp<IBinder>& displayToken, std::optional<gui::DisplayDecorationSupport>* outSupport) { + std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> support; + status_t status = mFlinger->getDisplayDecorationSupport(displayToken, &support); + if (status != NO_ERROR) { + ALOGE("getDisplayDecorationSupport failed with error %d", status); + return binderStatusFromStatusT(status); + } + + if (!support || !support.has_value()) { + outSupport->reset(); + } else { + outSupport->emplace(); + outSupport->value().format = static_cast<int32_t>(support->format); + outSupport->value().alphaInterpretation = + static_cast<int32_t>(support->alphaInterpretation); + } + + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::setOverrideFrameRate(int32_t uid, float frameRate) { + status_t status; + const int c_uid = IPCThreadState::self()->getCallingUid(); + if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) { + status = mFlinger->setOverrideFrameRate(uid, frameRate); + } else { + ALOGE("setOverrideFrameRate() permission denied for uid: %d", c_uid); + status = PERMISSION_DENIED; + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) { + *outPriority = mFlinger->getGpuContextPriority(); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::getMaxAcquiredBufferCount(int32_t* buffers) { + status_t status = mFlinger->getMaxAcquiredBufferCount(buffers); + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::addWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) { + status_t status; + const int uid = IPCThreadState::self()->getCallingUid(); + if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { + status = mFlinger->addWindowInfosListener(windowInfosListener); + } else { + status = PERMISSION_DENIED; + } + return binderStatusFromStatusT(status); +} - status = mFlinger->notifyPowerBoost(boostId); - return binder::Status::fromStatusT(status); +binder::Status SurfaceComposerAIDL::removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) { + status_t status; + const int uid = IPCThreadState::self()->getCallingUid(); + if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { + status = mFlinger->removeWindowInfosListener(windowInfosListener); + } else { + status = PERMISSION_DENIED; + } + return binderStatusFromStatusT(status); } status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) { @@ -7610,6 +7976,17 @@ status_t SurfaceComposerAIDL::checkControlDisplayBrightnessPermission() { return OK; } +status_t SurfaceComposerAIDL::checkReadFrameBufferPermission() { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS) && !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { + ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + return OK; +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 02846fef0d..c957b678e9 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -25,15 +25,18 @@ #include <android/gui/BnSurfaceComposer.h> #include <android/gui/DisplayStatInfo.h> #include <android/gui/DisplayState.h> +#include <android/gui/ISurfaceComposerClient.h> +#include <android/gui/ITransactionCompletedListener.h> #include <cutils/atomic.h> #include <cutils/compiler.h> #include <ftl/future.h> -#include <ftl/small_map.h> +#include <ftl/non_null.h> #include <gui/BufferQueue.h> +#include <gui/CompositorTiming.h> #include <gui/FrameTimestamps.h> #include <gui/ISurfaceComposer.h> -#include <gui/ISurfaceComposerClient.h> -#include <gui/ITransactionCompletedListener.h> +#include <gui/LayerDebugInfo.h> + #include <gui/LayerState.h> #include <layerproto/LayerProtoHeader.h> #include <math/mat4.h> @@ -50,26 +53,30 @@ #include <utils/Trace.h> #include <utils/threads.h> -#include <compositionengine/FenceResult.h> #include <compositionengine/OutputColorSetting.h> #include <scheduler/Fps.h> +#include <scheduler/PresentLatencyTracker.h> +#include <scheduler/Time.h> +#include <ui/FenceResult.h> -#include "ClientCache.h" +#include "Display/DisplayMap.h" +#include "Display/PhysicalDisplay.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/PowerAdvisor.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FlagManager.h" -#include "FrameTracker.h" +#include "FrontEnd/DisplayInfo.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncModulator.h" #include "SurfaceFlingerFactory.h" #include "ThreadContext.h" -#include "TracedOrdinal.h" #include "Tracing/LayerTracing.h" #include "Tracing/TransactionTracing.h" #include "TransactionCallbackInvoker.h" @@ -90,14 +97,15 @@ #include <unordered_map> #include <unordered_set> #include <utility> +#include <vector> #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> +#include "Client.h" using namespace android::surfaceflinger; namespace android { -class Client; class EventThread; class FlagManager; class FpsReporter; @@ -115,10 +123,13 @@ class FrameTracer; class ScreenCapturer; class WindowInfosListenerInvoker; +using frontend::TransactionHandler; using gui::CaptureArgs; using gui::DisplayCaptureArgs; using gui::IRegionSamplingListener; +using gui::ITransactionCompletedListener; using gui::LayerCaptureArgs; + using gui::ScreenCaptureResults; namespace frametimeline { @@ -166,24 +177,6 @@ enum class LatchUnsignaledConfig { using DisplayColorSetting = compositionengine::OutputColorSetting; -struct SurfaceFlingerBE { - // protected by mCompositorTimingLock; - mutable std::mutex mCompositorTimingLock; - CompositorTiming mCompositorTiming; - - // Only accessed from the main thread. - struct CompositePresentTime { - nsecs_t composite = -1; - std::shared_ptr<FenceTime> display = FenceTime::NO_FENCE; - }; - std::queue<CompositePresentTime> mCompositePresentTimes; - - static const size_t NUM_BUCKETS = 8; // < 1-7, 7+ - nsecs_t mFrameBuckets[NUM_BUCKETS] = {}; - nsecs_t mTotalTime = 0; - std::atomic<nsecs_t> mLastSwapTime = 0; -}; - class SurfaceFlinger : public BnSurfaceComposer, public PriorityDumper, private IBinder::DeathRecipient, @@ -204,29 +197,6 @@ public: static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } - // This is the phase offset in nanoseconds of the software vsync event - // relative to the vsync event reported by HWComposer. The software vsync - // event is when SurfaceFlinger and Choreographer-based applications run each - // frame. - // - // This phase offset allows adjustment of the minimum latency from application - // wake-up time (by Choreographer) to the time at which the resulting window - // image is displayed. This value may be either positive (after the HW vsync) - // or negative (before the HW vsync). Setting it to 0 will result in a lower - // latency bound of two vsync periods because the app and SurfaceFlinger - // will run just after the HW vsync. Setting it to a positive number will - // result in the minimum latency being: - // - // (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD)) - // - // Note that reducing this latency makes it more likely for the applications - // to not have their window content image ready in time. When this happens - // the latency will end up being an additional vsync period, and animations - // will hiccup. Therefore, this latency should be tuned somewhat - // conservatively (or at least with awareness of the trade-off being made). - static int64_t vsyncPhaseOffsetNs; - static int64_t sfVsyncPhaseOffsetNs; - // If fences from sync Framework are supported. static bool hasSyncFramework; @@ -249,10 +219,6 @@ public: static uint32_t maxGraphicsWidth; static uint32_t maxGraphicsHeight; - // Indicate if a device has wide color gamut display. This is typically - // found on devices with wide color gamut (e.g. Display-P3) display. - static bool hasWideColorDisplay; - // Indicate if device wants color management on its display. static const constexpr bool useColorManagement = true; @@ -280,9 +246,6 @@ public: // starts SurfaceFlinger main loop in the current thread void run() ANDROID_API; - SurfaceFlingerBE& getBE() { return mBE; } - const SurfaceFlingerBE& getBE() const { return mBE; } - // Indicates frame activity, i.e. whether commit and/or composite is taking place. enum class FrameHint { kNone, kActive }; @@ -309,9 +272,6 @@ public: renderengine::RenderEngine& getRenderEngine() const; - bool authenticateSurfaceTextureLocked( - const sp<IGraphicBufferProducer>& bufferProducer) const; - void onLayerFirstRef(Layer*); void onLayerDestroyed(Layer*); void onLayerUpdate(); @@ -319,6 +279,11 @@ public: void removeHierarchyFromOffscreenLayers(Layer* layer); void removeFromOffscreenLayers(Layer* layer); + // Called when all clients have released all their references to + // this layer. The layer may still be kept alive by its parents but + // the client can no longer modify this layer directly. + void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId); + // TODO: Remove atomic if move dtor to main thread CL lands std::atomic<uint32_t> mNumClones; @@ -326,16 +291,9 @@ public: return mTransactionCallbackInvoker; } - // Converts from a binder handle to a Layer - // Returns nullptr if the handle does not point to an existing layer. - // Otherwise, returns a weak reference so that callers off the main-thread - // won't accidentally hold onto the last strong reference. - wp<Layer> fromHandle(const sp<IBinder>& handle) const; - // If set, disables reusing client composition buffers. This can be set by // debug.sf.disable_client_composition_cache bool mDisableClientCompositionCache = false; - void windowInfosReported(); // Disables expensive rendering for all displays // This is scheduled on the main thread @@ -361,7 +319,7 @@ protected: REQUIRES(mStateLock); virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData( - const BufferData& bufferData, const char* layerName) const; + BufferData& bufferData, const char* layerName, uint64_t transactionId); // Returns true if any display matches a `bool(const DisplayDevice&)` predicate. template <typename Predicate> @@ -375,17 +333,15 @@ protected: private: friend class BufferLayer; - friend class BufferQueueLayer; - friend class BufferStateLayer; friend class Client; friend class FpsReporter; friend class TunnelModeEnabledReporter; friend class Layer; - friend class MonitoredProducer; friend class RefreshRateOverlay; friend class RegionSamplingThread; friend class LayerRenderArea; friend class LayerTracing; + friend class SurfaceComposerAIDL; // For unit tests friend class TestableSurfaceFlinger; @@ -399,10 +355,6 @@ private: using DumpArgs = Vector<String16>; using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>; - // This value is specified in number of frames. Log frame stats at most - // every half hour. - enum { LOG_FRAME_STATS_PERIOD = 30*60*60 }; - class State { public: explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {} @@ -422,7 +374,20 @@ private: const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid; LayerVector layersSortedByZ; - DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays; + + // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap. + DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays; + + std::optional<size_t> getDisplayIndex(PhysicalDisplayId displayId) const { + for (size_t i = 0; i < displays.size(); i++) { + const auto& state = displays.valueAt(i); + if (state.physical && state.physical->id == displayId) { + return i; + } + } + + return {}; + } bool colorMatrixChanged = true; mat4 colorMatrix; @@ -471,21 +436,12 @@ private: mCounterByLayerHandle GUARDED_BY(mLock); }; - using ActiveModeInfo = DisplayDevice::ActiveModeInfo; - using KernelIdleTimerController = - ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController; - enum class BootStage { BOOTLOADER, BOOTANIMATION, FINISHED, }; - struct HotplugEvent { - hal::HWDisplayId hwcDisplayId; - hal::Connection connection = hal::Connection::INVALID; - }; - template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr> static Dumper dumper(F&& dump) { using namespace std::placeholders; @@ -514,15 +470,15 @@ private: typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)> void modulateVsync(Handler handler, Args... args) { if (const auto config = (*mVsyncModulator.*handler)(args...)) { - const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs(); - setVsyncConfig(*config, vsyncPeriod); + setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod()); } } - static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB // Maximum allowed number of display frames that can be set through backdoor static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048; + static const size_t MAX_LAYERS = 4096; + // Implements IBinder. status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); } @@ -530,32 +486,28 @@ private: EXCLUDES(mStateLock); // Implements ISurfaceComposer - sp<ISurfaceComposerClient> createConnection() override; sp<IBinder> createDisplay(const String8& displayName, bool secure); void destroyDisplay(const sp<IBinder>& displayToken); std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) { Mutex::Autolock lock(mStateLock); return getPhysicalDisplayIdsLocked(); } - status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const EXCLUDES(mStateLock); sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const; status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, + Vector<ComposerState>& state, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) override; - void bootFinished() override; - bool authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& bufferProducer) const override; - status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override; + void bootFinished(); + virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const; sp<IDisplayEventConnection> createDisplayEventConnection( - ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override; + gui::ISurfaceComposer::VsyncSource vsyncSource = + gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, + EventRegistrationFlags eventRegistration = {}); status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); @@ -564,63 +516,50 @@ private: status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) EXCLUDES(mStateLock); - status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*) - EXCLUDES(mStateLock) override; - status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*) - EXCLUDES(mStateLock) override; - status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, - ui::DisplayPrimaries&) override; - status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override; + status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock); + status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*) + EXCLUDES(mStateLock); + status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken, + ui::DynamicDisplayInfo*) EXCLUDES(mStateLock); + void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&, + const display::DisplaySnapshot&); + status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&); + status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode); status_t getBootDisplayModeSupport(bool* outSupport) const; - status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) override; + status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId); + status_t getOverlaySupport(gui::OverlayProperties* outProperties) const; status_t clearBootDisplayMode(const sp<IBinder>& displayToken); void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on); void setGameContentType(const sp<IBinder>& displayToken, bool on); void setPowerMode(const sp<IBinder>& displayToken, int mode); - status_t clearAnimationFrameStats() override; - status_t getAnimationFrameStats(FrameStats* outStats) const override; status_t overrideHdrTypes(const sp<IBinder>& displayToken, - const std::vector<ui::Hdr>& hdrTypes) override; - status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) override; - status_t enableVSyncInjections(bool enable) override; - status_t injectVSync(nsecs_t when) override; - status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override; - status_t getColorManagement(bool* outGetColorManagement) const override; + const std::vector<ui::Hdr>& hdrTypes); + status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success); + status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers); + status_t getColorManagement(bool* outGetColorManagement) const; status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, ui::Dataspace* outWideColorGamutDataspace, - ui::PixelFormat* outWideColorGamutPixelFormat) const override; + ui::PixelFormat* outWideColorGamutPixelFormat) const; status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, - uint8_t* outComponentMask) const override; + uint8_t* outComponentMask) const; status_t setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken, bool enable, - uint8_t componentMask, uint64_t maxFrames) override; + uint8_t componentMask, uint64_t maxFrames); status_t getDisplayedContentSample(const sp<IBinder>& displayToken, uint64_t maxFrames, - uint64_t timestamp, - DisplayedFrameStats* outStats) const override; - status_t getProtectedContentSupport(bool* outSupported) const override; + uint64_t timestamp, DisplayedFrameStats* outStats) const; + status_t getProtectedContentSupport(bool* outSupported) const; status_t isWideColorDisplay(const sp<IBinder>& displayToken, bool* outIsWideColorDisplay) const; 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 addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override; - status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override; - status_t addTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) override; - status_t removeTunnelModeEnabledListener( - const sp<gui::ITunnelModeEnabledListener>& listener) override; + const sp<IRegionSamplingListener>& listener); + status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener); + status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener); + status_t removeFpsListener(const sp<gui::IFpsListener>& listener); + status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener); + status_t removeTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener); status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId displayModeId, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) override; - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) override; + const gui::DisplayModeSpecs&); + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs*); status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const; status_t setDisplayBrightness(const sp<IBinder>& displayToken, const gui::DisplayBrightness& brightness); @@ -630,36 +569,32 @@ private: const sp<gui::IHdrLayerInfoListener>& listener); status_t notifyPowerBoost(int32_t boostId); status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, - float lightPosY, float lightPosZ, float lightRadius) override; + float lightPosY, float lightPosZ, float lightRadius); status_t getDisplayDecorationSupport( const sp<IBinder>& displayToken, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* - outSupport) const override; + outSupport) const; status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility, int8_t changeFrameRateStrategy) override; + int8_t compatibility, int8_t changeFrameRateStrategy); status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, - const FrameTimelineInfo& frameTimelineInfo) override; + const gui::FrameTimelineInfo& frameTimelineInfo); - status_t setOverrideFrameRate(uid_t uid, float frameRate) override; + status_t setOverrideFrameRate(uid_t uid, float frameRate); - status_t addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) override; + int getGpuContextPriority(); - int getGPUContextPriority() override; + status_t getMaxAcquiredBufferCount(int* buffers) const; - status_t getMaxAcquiredBufferCount(int* buffers) const override; - - status_t addWindowInfosListener( - const sp<gui::IWindowInfosListener>& windowInfosListener) const override; + status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener) const; status_t removeWindowInfosListener( - const sp<gui::IWindowInfosListener>& windowInfosListener) const override; + const sp<gui::IWindowInfosListener>& windowInfosListener) const; // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; // HWC2::ComposerCallback overrides: - void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp, + void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp, std::optional<hal::VsyncPeriodNanos>) override; void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override; void onComposerHalRefresh(hal::HWDisplayId) override; @@ -670,31 +605,34 @@ private: // ICompositor overrides: + // Configures physical displays, processing hotplug and/or mode setting via the Composer HAL. + void configure() override; + // Commits transactions for layers and displays. Returns whether any state has been invalidated, // i.e. whether a frame should be composited for each display. - bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) override; + bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override; // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition // via RenderEngine and the Composer HAL, respectively. - void composite(nsecs_t frameTime, int64_t vsyncId) override; + void composite(TimePoint frameTime, VsyncId) override; // Samples the composited frame via RegionSamplingThread. void sample() override; - /* - * ISchedulerCallback - */ + // ISchedulerCallback overrides: // Toggles hardware VSYNC by calling into HWC. + // TODO(b/241286146): Rename for self-explanatory API. void setVsyncEnabled(bool) override; - // Sets the desired display mode if allowed by policy. - void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override; - // Called when kernel idle timer has expired. Used to update the refresh rate overlay. + void requestDisplayModes(std::vector<display::DisplayModeRequest>) override; void kernelTimerChanged(bool expired) override; - // Called when the frame rate override list changed to trigger an event. void triggerOnFrameRateOverridesChanged() override; + // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); + + using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; + // Get the controller and timeout that will help decide how the kernel idle timer will be // configured and what value to use as the timeout. std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> @@ -708,41 +646,51 @@ private: bool mKernelIdleTimerEnabled = false; // Show spinner with refresh rate overlay bool mRefreshRateOverlaySpinner = false; + // Show render rate with refresh rate overlay + bool mRefreshRateOverlayRenderRate = false; - // Called on the main thread in response to initializeDisplays() - void onInitializeDisplays() REQUIRES(mStateLock); - // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode. - void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock); - status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id); + void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); + + status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId); // Sets the active mode and a new refresh rate in SF. - void updateInternalStateWithChangedMode() REQUIRES(mStateLock); + void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext); // Calls to setActiveMode on the main thread if there is a pending mode change // that needs to be applied. - void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock); + void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext); void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock); // Called when active mode is no longer is progress void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock); // Called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) - REQUIRES(mStateLock); + REQUIRES(mStateLock, kMainThreadContext); // Returns true if the display has a visible HDR layer in its layer stack. bool hasVisibleHdrLayer(const sp<DisplayDevice>& display) REQUIRES(mStateLock); - // Sets the desired display mode specs. + // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that + // display. Falls back to the display's defaultModeId otherwise. + ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode( + PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock); + status_t setDesiredDisplayModeSpecsInternal( - const sp<DisplayDevice>& display, - const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) - EXCLUDES(mStateLock); + const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&) + EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + + // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter. + status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId, + const scheduler::RefreshRateSelector&) + REQUIRES(mStateLock, kMainThreadContext); - void commitTransactions() EXCLUDES(mStateLock); - void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock); + void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + void commitTransactionsLocked(uint32_t transactionFlags) + REQUIRES(mStateLock, kMainThreadContext); void doCommitTransactions() REQUIRES(mStateLock); // Returns whether a new buffer has been latched. bool latchBuffers(); void updateLayerGeometry(); + void updateLayerMetadataSnapshot(); void updateInputFlinger(); void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext); @@ -751,7 +699,7 @@ private: void commitInputWindowCommands() REQUIRES(mStateLock); void updateCursorAsync(); - void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock); + void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock); void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock); void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod); @@ -759,8 +707,9 @@ private: /* * Transactions */ - bool applyTransactionState(const FrameTimelineInfo& info, Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, + bool applyTransactionState(const FrameTimelineInfo& info, + std::vector<ResolvedComposerState>& state, + Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime, @@ -768,26 +717,22 @@ private: const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock); - // flush pending transaction that was presented after desiredPresentTime. - bool flushTransactionQueues(int64_t vsyncId); + // Flush pending transactions that were presented after desiredPresentTime. + bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext); // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); - - int flushPendingTransactionQueues( - std::vector<TransactionState>& transactions, - std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions, - bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock); - - int flushUnsignaledPendingTransactionQueues( - std::vector<TransactionState>& transactions, - std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions) - REQUIRES(mStateLock, mQueueLock); - - uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&, + void addTransactionReadyFilters(); + TransactionHandler::TransactionReadiness transactionReadyTimelineCheck( + const TransactionHandler::TransactionFlushState& flushState) + REQUIRES(kMainThreadContext); + TransactionHandler::TransactionReadiness transactionReadyBufferCheck( + const TransactionHandler::TransactionFlushState& flushState) + REQUIRES(kMainThreadContext); + + uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&, int64_t desiredPresentTime, bool isAutoTimestamp, - int64_t postTime, uint32_t permissions) REQUIRES(mStateLock); + int64_t postTime, uint32_t permissions, uint64_t transactionId) + REQUIRES(mStateLock); uint32_t getTransactionFlags() const; @@ -801,40 +746,20 @@ private: void commitOffscreenLayers(); - enum class TransactionReadiness { - NotReady, - NotReadyBarrier, - Ready, - ReadyUnsignaled, - }; - TransactionReadiness transactionIsReadyToBeApplied(TransactionState& state, - const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, - uid_t originUid, const Vector<ComposerState>& states, - const std::unordered_map< - sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent, - size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock); static LatchUnsignaledConfig getLatchUnsignaledConfig(); bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates, - size_t totalTXapplied) const; - bool stopTransactionProcessing(const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& - applyTokensWithUnsignaledTransactions) const; - bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId) + bool firstTransaction) const; + bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); - bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const; + bool frameIsEarly(TimePoint expectedPresentTime, VsyncId) const; + /* * Layer management */ - status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, - const sp<IBinder>& parentHandle, int32_t* outLayerId, - const sp<Layer>& parentLayer = nullptr, - uint32_t* outTransformHint = nullptr); - - status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + status_t createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult); status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, sp<Layer>* outLayer); @@ -842,21 +767,17 @@ private: status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle, sp<Layer>* outLayer); - status_t createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle, - sp<Layer>* outLayer); - status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle, - sp<IBinder>* outHandle, int32_t* outLayerId); + gui::CreateSurfaceResult& outResult); + + status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args, + gui::CreateSurfaceResult& outResult); - // called when all clients have released all their references to - // this layer meaning it is entirely safe to destroy all - // resources associated to this layer. - void onHandleDestroyed(BBinder* handle, sp<Layer>& layer); void markLayerPendingRemovalLocked(const sp<Layer>& layer); // add a layer to SurfaceFlinger - status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, - const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot, + status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle, + const sp<Layer>& layer, const wp<Layer>& parentLayer, uint32_t* outTransformHint); // Traverse through all the layers and compute and cache its bounds. @@ -874,9 +795,10 @@ private: const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> renderScreenImpl( - const RenderArea&, TraverseLayersFunction, + std::unique_ptr<RenderArea>, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent, - bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock); + bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock) + REQUIRES(kMainThreadContext); // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a // matching ownerUid @@ -889,8 +811,10 @@ private: /* * Display and layer stack management */ - // called when starting, or restarting after system_server death + + // Called during boot, and restart after system_server death. void initializeDisplays(); + void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext); sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const REQUIRES(mStateLock) { @@ -926,12 +850,12 @@ private: } sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) { - if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) { + if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) { return display; } // The active display is outdated, so fall back to the primary display. - mActiveDisplayToken.clear(); - return getDisplayDeviceLocked(getPrimaryDisplayTokenLocked()); + mActiveDisplayId = getPrimaryDisplayIdLocked(); + return getDisplayDeviceLocked(mActiveDisplayId); } sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) { @@ -939,6 +863,21 @@ private: return getDefaultDisplayDeviceLocked(); } + using DisplayDeviceAndSnapshot = + std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>; + + // Combinator for ftl::Optional<PhysicalDisplay>::and_then. + auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) { + return [this](const display::PhysicalDisplay& display) REQUIRES( + mStateLock) -> ftl::Optional<DisplayDeviceAndSnapshot> { + if (auto device = getDisplayDeviceLocked(display.snapshot().displayId())) { + return std::make_pair(std::move(device), display.snapshotRef()); + } + + return {}; + }; + } + // Returns the first display that matches a `bool(const DisplayDevice&)` predicate. template <typename Predicate> sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) { @@ -954,8 +893,13 @@ private: // region of all screens presenting this layer stack. void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty); - bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) { - return display->getDisplayToken() == mActiveDisplayToken; + ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack) + REQUIRES(mStateLock) { + return {layerStack, + PhysicalDisplayId::tryCast(displayId) + .and_then(display::getPhysicalDisplay(mPhysicalDisplays)) + .transform(&display::PhysicalDisplay::isInternal) + .value_or(false)}; } /* @@ -972,14 +916,7 @@ private: /* * Compositing */ - void postComposition(); - void getCompositorTiming(CompositorTiming* compositorTiming); - void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, - std::shared_ptr<FenceTime>& presentFenceTime); - void setCompositorTimingSnapped(const DisplayStatInfo& stats, - nsecs_t compositeToPresentLatency); - - void postFrame() REQUIRES(kMainThreadContext); + void postComposition() REQUIRES(kMainThreadContext); /* * Display management @@ -987,18 +924,31 @@ private: std::pair<DisplayModes, DisplayModePtr> loadDisplayModes(PhysicalDisplayId) const REQUIRES(mStateLock); + // TODO(b/241285876): Move to DisplayConfigurator. + // + // Returns whether displays have been added/changed/removed, i.e. whether ICompositor should + // commit display transactions. + bool configureLocked() REQUIRES(mStateLock) REQUIRES(kMainThreadContext) + EXCLUDES(mHotplugMutex); + + // Returns a string describing the hotplug, or nullptr if it was rejected. + const char* processHotplug(PhysicalDisplayId, hal::HWDisplayId, bool connected, + DisplayIdentificationInfo&&) REQUIRES(mStateLock) + REQUIRES(kMainThreadContext); + sp<DisplayDevice> setupNewDisplayDeviceInternal( const wp<IBinder>& displayToken, std::shared_ptr<compositionengine::Display> compositionDisplay, const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock); - void processDisplayChangesLocked() REQUIRES(mStateLock); - void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock); + void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext); + void processDisplayRemoved(const wp<IBinder>& displayToken) + REQUIRES(mStateLock, kMainThreadContext); void processDisplayChanged(const wp<IBinder>& displayToken, const DisplayDeviceState& currentState, - const DisplayDeviceState& drawingState) REQUIRES(mStateLock); - void processDisplayHotplugEventsLocked() REQUIRES(mStateLock); + const DisplayDeviceState& drawingState) + REQUIRES(mStateLock, kMainThreadContext); void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected); @@ -1012,50 +962,31 @@ private: getHwComposer().setVsyncEnabled(id, enabled); } - struct FenceWithFenceTime { - sp<Fence> fence = Fence::NO_FENCE; - std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE; - }; - - // Gets the fence for the previous frame. - // Must be called on the main thread. - FenceWithFenceTime previousFrameFence(); + using FenceTimePtr = std::shared_ptr<FenceTime>; - // Whether the previous frame has not yet been presented to the display. - // If graceTimeMs is positive, this method waits for at most the provided - // grace period before reporting if the frame missed. - // Must be called on the main thread. - bool previousFramePending(int graceTimeMs = 0); + const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) + REQUIRES(kMainThreadContext); - // Returns the previous time that the frame was presented. If the frame has - // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there - // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID. - // Must be called on the main thread. - nsecs_t previousFramePresentTime(); + // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal. + static bool isFencePending(const FenceTimePtr&, int graceTimeMs); // Calculates the expected present time for this frame. For negative offsets, performs a // correction using the predicted vsync for the next frame instead. - - nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const; + TimePoint calculateExpectedPresentTime(TimePoint frameTime) const; /* * Display identification */ - sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const + sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const REQUIRES(mStateLock) { - const sp<IBinder> nullToken; - return mPhysicalDisplayTokens.get(displayId).value_or(std::cref(nullToken)); + const sp<display::DisplayToken> nullToken; + return mPhysicalDisplays.get(displayId) + .transform([](const display::PhysicalDisplay& display) { return display.token(); }) + .value_or(std::cref(nullToken)); } std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked( - const sp<IBinder>& displayToken) const REQUIRES(mStateLock) { - for (const auto& [id, token] : mPhysicalDisplayTokens) { - if (token == displayToken) { - return id; - } - } - return {}; - } + const sp<display::DisplayToken>&) const REQUIRES(mStateLock); // Returns the first display connected at boot. // @@ -1078,15 +1009,20 @@ private: VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock); void releaseVirtualDisplay(VirtualDisplayId); - void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock); + // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr` + // in tests, and `activeDisplay` must not be `nullptr` as a precondition. + void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay, + const sp<DisplayDevice>& activeDisplay) + REQUIRES(mStateLock, kMainThreadContext); - void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay); + void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&); /* * Debugging & dumpsys */ void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, std::string& result) const REQUIRES(mStateLock); + void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; void listLayersLocked(std::string& result) const; @@ -1094,10 +1030,11 @@ private: void clearStatsLocked(const DumpArgs& args, std::string& result); void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; - void logFrameStats() REQUIRES(kMainThreadContext); + void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext); - void dumpVSync(std::string& result) const REQUIRES(mStateLock); - void dumpStaticScreenStats(std::string& result) const; + void dumpScheduler(std::string& result) const REQUIRES(mStateLock); + void dumpEvents(std::string& result) const REQUIRES(mStateLock); + void dumpVsync(std::string& result) const REQUIRES(mStateLock); void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock); void dumpDisplays(std::string& result) const REQUIRES(mStateLock); @@ -1134,26 +1071,18 @@ private: status_t CheckTransactCodeCredentials(uint32_t code); // Add transaction to the Transaction Queue - void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock); - void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal); - void signalSynchronousTransactions(const uint32_t flag); /* * Generic Layer Metadata */ const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const; - /* - * Misc - */ - std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock); - static int calculateMaxAcquiredBufferCount(Fps refreshRate, std::chrono::nanoseconds presentLatency); int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const; void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) - REQUIRES(mStateLock); + REQUIRES(mStateLock, kMainThreadContext); bool isHdrLayer(Layer* layer) const; @@ -1169,17 +1098,19 @@ private: mutable Mutex mStateLock; State mCurrentState{LayerVector::StateSet::Current}; std::atomic<int32_t> mTransactionFlags = 0; - std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals; - bool mAnimTransactionPending = false; std::atomic<uint32_t> mUniqueTransactionId = 1; SortedVector<sp<Layer>> mLayersPendingRemoval; + // Buffers that have been discarded by clients and need to be evicted from per-layer caches so + // the graphics memory can be immediately freed. + std::vector<uint64_t> mBufferIdsToUncache; + // global color transform states Daltonizer mDaltonizer; float mGlobalSaturationFactor = 1.0f; mat4 mClientColorMatrix; - size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS; + size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS; // If there are more GraphicBufferProducers tracked by SurfaceFlinger than // this threshold, then begin logging. size_t mGraphicBufferProducerListSizeLogThreshold = @@ -1194,7 +1125,6 @@ private: // constant members (no synchronization needed for access) const nsecs_t mBootTime = systemTime(); - bool mGpuToCpuSupported = false; bool mIsUserBuild = true; // Can only accessed from the main thread, these members @@ -1213,20 +1143,20 @@ private: // Set during transaction application stage to track if the input info or children // for a layer has changed. // TODO: Also move visibleRegions over to a boolean system. - bool mInputInfoChanged = false; + bool mUpdateInputInfo = false; bool mSomeChildrenChanged; bool mSomeDataspaceChanged = false; bool mForceTransactionDisplayChange = false; - bool mAnimCompositionPending = false; + // Set if LayerMetadata has changed since the last LayerMetadata snapshot. + bool mLayerMetadataSnapshotNeeded = false; // Tracks layers that have pending frames which are candidates for being // latched. std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames; // Tracks layers that need to update a display's dirty region. std::vector<sp<Layer>> mLayersPendingRefresh; - // size should be longest sf-duration / shortest vsync period and round up - std::array<FenceWithFenceTime, 5> mPreviousPresentFences; // currently consider 166hz. + // 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. @@ -1240,17 +1170,24 @@ private: BootStage mBootStage = BootStage::BOOTLOADER; - std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock); + struct HotplugEvent { + hal::HWDisplayId hwcDisplayId; + hal::Connection connection = hal::Connection::INVALID; + }; + + std::mutex mHotplugMutex; + std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex); // Displays are composited in `mDisplays` order. Internal displays are inserted at boot and // never removed, so take precedence over external and virtual displays. // - // The static capacities were chosen to exceed a typical number of physical/virtual displays. - // // May be read from any thread, but must only be written from the main thread. - ftl::SmallMap<wp<IBinder>, const sp<DisplayDevice>, 5> mDisplays GUARDED_BY(mStateLock); - ftl::SmallMap<PhysicalDisplayId, const sp<IBinder>, 3> mPhysicalDisplayTokens - GUARDED_BY(mStateLock); + display::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock); + + display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock); + + // The inner or outer display for foldables, assuming they have mutually exclusive power states. + PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock); struct { DisplayIdGenerator<GpuVirtualDisplayId> gpu; @@ -1265,7 +1202,6 @@ private: bool mLayerCachingEnabled = false; bool mPropagateBackpressureClientComposition = false; - sp<SurfaceInterceptor> mInterceptor; LayerTracing mLayerTracing{*this}; bool mLayerTracingEnabled = false; @@ -1277,6 +1213,8 @@ private: const std::unique_ptr<FrameTracer> mFrameTracer; const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline; + VsyncId mLastCommittedVsyncId; + // If blurs should be enabled on this device. bool mSupportsBlur = false; // If blurs are considered expensive and should require high GPU frequency. @@ -1287,9 +1225,6 @@ private: TransactionCallbackInvoker mTransactionCallbackInvoker; - // Thread-safe. - FrameTracker mAnimFrameTracker; - // We maintain a pool of pre-generated texture names to hand out to avoid // layer creation needing to run on the main thread (which it would // otherwise need to do to access RenderEngine). @@ -1297,18 +1232,6 @@ private: uint32_t mTexturePoolSize = 0; std::vector<uint32_t> mTexturePool; - mutable Mutex mQueueLock; - Condition mTransactionQueueCV; - std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> - mPendingTransactionQueues GUARDED_BY(mQueueLock); - std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock); - /* - * Feature prototyping - */ - - // Static screen stats - bool mHasPoweredOff = false; - std::atomic<size_t> mNumLayers = 0; // to linkToDeath @@ -1331,12 +1254,16 @@ private: // This property can be used to force SurfaceFlinger to always pick a certain color mode. ui::ColorMode mForceColorMode = ui::ColorMode::NATIVE; + // Whether to enable wide color gamut (e.g. Display P3) for internal displays that support it. + // If false, wide color modes are filtered out for all internal displays. + bool mSupportsWideColor = false; + ui::Dataspace mDefaultCompositionDataspace; ui::Dataspace mWideColorGamutCompositionDataspace; ui::Dataspace mColorSpaceAgnosticDataspace; float mDimmingRatio = -1.f; - SurfaceFlingerBE mBE; + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by // any mutex. @@ -1344,8 +1271,6 @@ private: const std::string mHwcServiceName; - bool hasMockHwc() const { return mHwcServiceName == "mock"; } - /* * Scheduler */ @@ -1360,9 +1285,17 @@ private: sp<VsyncModulator> mVsyncModulator; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; + scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext); + + struct FenceWithFenceTime { + sp<Fence> fence = Fence::NO_FENCE; + FenceTimePtr fenceTime = FenceTime::NO_FENCE; + }; + std::array<FenceWithFenceTime, 2> mPreviousPresentFences; + + TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext); + TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext); - std::atomic<nsecs_t> mExpectedPresentTime = 0; - nsecs_t mScheduledPresentTime = 0; hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE; hal::Vsync mLastHWCVsyncState = hal::Vsync::DISABLE; @@ -1384,7 +1317,7 @@ private: std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor; - void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock); + void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext); // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; @@ -1416,8 +1349,21 @@ private: // A temporay pool that store the created layers and will be added to current state in main // thread. std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock); - bool commitCreatedLayers(); - void handleLayerCreatedLocked(const LayerCreatedState& state) REQUIRES(mStateLock); + bool commitCreatedLayers(VsyncId); + void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock); + + mutable std::mutex mMirrorDisplayLock; + struct MirrorDisplayState { + MirrorDisplayState(ui::LayerStack layerStack, sp<IBinder>& rootHandle, + const sp<Client>& client) + : layerStack(layerStack), rootHandle(rootHandle), client(client) {} + + ui::LayerStack layerStack; + sp<IBinder> rootHandle; + const sp<Client> client; + }; + std::vector<MirrorDisplayState> mMirrorDisplays GUARDED_BY(mMirrorDisplayLock); + bool commitMirrorDisplays(VsyncId); std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; @@ -1426,8 +1372,6 @@ private: [](const auto& display) { return display.isRefreshRateOverlayEnabled(); }); } - wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock); - const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker; FlagManager mFlagManager; @@ -1444,28 +1388,43 @@ private: bool early = false; } mPowerHintSessionMode; - nsecs_t mAnimationTransactionTimeout = s2ns(5); - - friend class SurfaceComposerAIDL; + TransactionHandler mTransactionHandler; + display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos; }; class SurfaceComposerAIDL : public gui::BnSurfaceComposer { public: - SurfaceComposerAIDL(sp<SurfaceFlinger> sf) { mFlinger = sf; } + SurfaceComposerAIDL(sp<SurfaceFlinger> sf) : mFlinger(std::move(sf)) {} + binder::Status bootFinished() override; + binder::Status createDisplayEventConnection( + VsyncSource vsyncSource, EventRegistration eventRegistration, + sp<gui::IDisplayEventConnection>* outConnection) override; + binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override; binder::Status createDisplay(const std::string& displayName, bool secure, sp<IBinder>* outDisplay) override; binder::Status destroyDisplay(const sp<IBinder>& display) override; binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override; - binder::Status getPrimaryPhysicalDisplayId(int64_t* outDisplayId) override; binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override; binder::Status setPowerMode(const sp<IBinder>& display, int mode) override; + binder::Status getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) override; binder::Status getDisplayStats(const sp<IBinder>& display, gui::DisplayStatInfo* outStatInfo) override; binder::Status getDisplayState(const sp<IBinder>& display, gui::DisplayState* outState) override; + binder::Status getStaticDisplayInfo(int64_t displayId, + gui::StaticDisplayInfo* outInfo) override; + binder::Status getDynamicDisplayInfoFromId(int64_t displayId, + gui::DynamicDisplayInfo* outInfo) override; + binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display, + gui::DynamicDisplayInfo* outInfo) override; + binder::Status getDisplayNativePrimaries(const sp<IBinder>& display, + gui::DisplayPrimaries* outPrimaries) override; + binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override; + binder::Status setBootDisplayMode(const sp<IBinder>& display, int displayModeId) override; binder::Status clearBootDisplayMode(const sp<IBinder>& display) override; binder::Status getBootDisplayModeSupport(bool* outMode) override; + binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override; binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override; binder::Status setGameContentType(const sp<IBinder>& display, bool on) override; binder::Status captureDisplay(const DisplayCaptureArgs&, @@ -1473,8 +1432,47 @@ public: binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override; binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override; + + // TODO(b/239076119): Remove deprecated AIDL. + [[deprecated]] binder::Status clearAnimationFrameStats() override { + return binder::Status::ok(); + } + [[deprecated]] binder::Status getAnimationFrameStats(gui::FrameStats*) override { + return binder::Status::ok(); + } + + binder::Status overrideHdrTypes(const sp<IBinder>& display, + const std::vector<int32_t>& hdrTypes) override; + binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override; + binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override; + binder::Status getColorManagement(bool* outGetColorManagement) override; + binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override; + binder::Status getDisplayedContentSamplingAttributes( + const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override; + binder::Status setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + int8_t componentMask, + int64_t maxFrames) override; + binder::Status getDisplayedContentSample(const sp<IBinder>& display, int64_t maxFrames, + int64_t timestamp, + gui::DisplayedFrameStats* outStats) override; + binder::Status getProtectedContentSupport(bool* outSupporte) override; binder::Status isWideColorDisplay(const sp<IBinder>& token, bool* outIsWideColorDisplay) override; + binder::Status addRegionSamplingListener( + const gui::ARect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<gui::IRegionSamplingListener>& listener) override; + binder::Status removeRegionSamplingListener( + const sp<gui::IRegionSamplingListener>& listener) override; + binder::Status addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override; + binder::Status removeFpsListener(const sp<gui::IFpsListener>& listener) override; + binder::Status addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) override; + binder::Status removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) override; + binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs&) override; + binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + gui::DisplayModeSpecs* outSpecs) override; binder::Status getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) override; binder::Status setDisplayBrightness(const sp<IBinder>& displayToken, @@ -1485,11 +1483,27 @@ public: const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) override; binder::Status notifyPowerBoost(int boostId) override; + binder::Status setGlobalShadowSettings(const gui::Color& ambientColor, + const gui::Color& spotColor, float lightPosY, + float lightPosZ, float lightRadius) override; + binder::Status getDisplayDecorationSupport( + const sp<IBinder>& displayToken, + std::optional<gui::DisplayDecorationSupport>* outSupport) override; + binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override; + binder::Status getGpuContextPriority(int32_t* outPriority) override; + binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override; + binder::Status addWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) override; + binder::Status removeWindowInfosListener( + const sp<gui::IWindowInfosListener>& windowInfosListener) override; private: static const constexpr bool kUsePermissionCache = true; status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache); status_t checkControlDisplayBrightnessPermission(); + status_t checkReadFrameBufferPermission(); + static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info, + gui::DynamicDisplayInfo*& outInfo); private: sp<SurfaceFlinger> mFlinger; diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp index b81b445dad..7e6894d3f7 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -22,22 +22,16 @@ #include <cutils/properties.h> #include <ui/GraphicBuffer.h> -#include "BufferLayerConsumer.h" -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" -#include "ContainerLayer.h" #include "DisplayDevice.h" -#include "EffectLayer.h" #include "FrameTracer/FrameTracer.h" #include "Layer.h" -#include "MonitoredProducer.h" #include "NativeWindowSurface.h" #include "StartPropertySetThread.h" #include "SurfaceFlingerDefaultFactory.h" #include "SurfaceFlingerProperties.h" -#include "SurfaceInterceptor.h" #include "DisplayHardware/ComposerHal.h" +#include "FrameTimeline/FrameTimeline.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncController.h" @@ -59,23 +53,19 @@ std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfig } } -sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() { - return new android::impl::SurfaceInterceptor(); -} - sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread( bool timestampPropertyValue) { - return new StartPropertySetThread(timestampPropertyValue); + return sp<StartPropertySetThread>::make(timestampPropertyValue); } sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) { - return new DisplayDevice(creationArgs); + return sp<DisplayDevice>::make(creationArgs); } sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, std::string requestorName) { - return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName); } void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, @@ -84,18 +74,6 @@ void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); } -sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer( - const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger, - const wp<Layer>& layer) { - return new MonitoredProducer(producer, flinger, layer); -} - -sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer( - const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine, - uint32_t textureName, Layer* layer) { - return new BufferLayerConsumer(consumer, renderEngine, textureName, layer); -} - std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface( const sp<IGraphicBufferProducer>& producer) { return surfaceflinger::impl::createNativeWindowSurface(producer); @@ -105,20 +83,16 @@ std::unique_ptr<compositionengine::CompositionEngine> DefaultFactory::createComp return compositionengine::impl::createCompositionEngine(); } -sp<ContainerLayer> DefaultFactory::createContainerLayer(const LayerCreationArgs& args) { - return new ContainerLayer(args); -} - -sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) { - return new BufferQueueLayer(args); +sp<Layer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) { + return sp<Layer>::make(args); } -sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) { - return new BufferStateLayer(args); +sp<Layer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) { + return sp<Layer>::make(args); } -sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) { - return new EffectLayer(args); +sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName) { + return sp<LayerFE>::make(layerName); } std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() { diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h index 501629d4da..2c6de0e113 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -29,7 +29,6 @@ public: std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override; std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) override; - sp<SurfaceInterceptor> createSurfaceInterceptor() override; sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override; sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override; sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, @@ -38,19 +37,12 @@ public: void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) override; - sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&, - const sp<SurfaceFlinger>&, - const wp<Layer>&) override; - sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&, - renderengine::RenderEngine&, uint32_t tex, - Layer*) override; std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( const sp<IGraphicBufferProducer>&) override; std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override; - sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override; - sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override; - sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override; - sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override; + sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override; + sp<Layer> createEffectLayer(const LayerCreationArgs& args) override; + sp<LayerFE> createLayerFE(const std::string& layerName) override; std::unique_ptr<FrameTracer> createFrameTracer() override; std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline( std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override; diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp index 3997b04f5f..7bd6cf69f3 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -26,7 +26,7 @@ namespace android::surfaceflinger { sp<SurfaceFlinger> createSurfaceFlinger() { static DefaultFactory factory; - return new SurfaceFlinger(factory); + return sp<SurfaceFlinger>::make(factory); } } // namespace android::surfaceflinger diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index 6153e8e354..f310c4ac53 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -30,25 +30,20 @@ namespace android { typedef int32_t PixelFormat; -class BufferQueueLayer; class BufferLayerConsumer; -class BufferStateLayer; -class ContainerLayer; class DisplayDevice; -class EffectLayer; class FrameTracer; class GraphicBuffer; class HWComposer; class IGraphicBufferConsumer; class IGraphicBufferProducer; class Layer; +class LayerFE; class StartPropertySetThread; class SurfaceFlinger; -class SurfaceInterceptor; class TimeStats; struct DisplayDeviceCreationArgs; -struct LayerCreationArgs; namespace compositionengine { class CompositionEngine; @@ -57,7 +52,6 @@ class CompositionEngine; namespace scheduler { class VsyncConfiguration; class VsyncController; -class RefreshRateConfigs; } // namespace scheduler namespace frametimeline { @@ -66,6 +60,7 @@ class FrameTimeline; namespace surfaceflinger { +struct LayerCreationArgs; class NativeWindowSurface; // The interface that SurfaceFlinger uses to create all of the implementations @@ -75,7 +70,6 @@ public: virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0; virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) = 0; - virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0; virtual sp<StartPropertySetThread> createStartPropertySetThread( bool timestampPropertyValue) = 0; @@ -86,22 +80,15 @@ public: virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) = 0; - virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&, - const sp<SurfaceFlinger>&, - const wp<Layer>&) = 0; - virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&, - renderengine::RenderEngine&, - uint32_t tex, Layer*) = 0; virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( const sp<IGraphicBufferProducer>&) = 0; virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0; - virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0; - virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0; - virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0; - virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0; + virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0; + virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0; + virtual sp<LayerFE> createLayerFE(const std::string& layerName) = 0; virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0; virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline( std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 20fa091730..5b7303090d 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -367,6 +367,14 @@ bool enable_frame_rate_override(bool defaultValue) { return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue); } +bool frame_rate_override_for_native_rates(bool defaultValue) { + return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue); +} + +bool frame_rate_override_global(bool defaultValue) { + return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue); +} + bool enable_layer_caching(bool defaultValue) { return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue); } diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 080feee686..09629cf93e 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -96,6 +96,10 @@ bool update_device_product_info_on_hotplug_reconnect(bool defaultValue); bool enable_frame_rate_override(bool defaultValue); +bool frame_rate_override_for_native_rates(bool defaultValue); + +bool frame_rate_override_global(bool defaultValue); + bool enable_layer_caching(bool defaultValue); bool enable_sdr_dimming(bool defaultValue); diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp deleted file mode 100644 index 0782fef8ea..0000000000 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ /dev/null @@ -1,719 +0,0 @@ -/* - * Copyright 2016 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#undef LOG_TAG -#define LOG_TAG "SurfaceInterceptor" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "Layer.h" -#include "SurfaceFlinger.h" -#include "SurfaceInterceptor.h" - -#include <fstream> - -#include <android-base/file.h> -#include <log/log.h> -#include <utils/Trace.h> - -namespace android { - -// ---------------------------------------------------------------------------- -// TODO(marissaw): add new layer state values to SurfaceInterceptor - -SurfaceInterceptor::~SurfaceInterceptor() = default; - -namespace impl { - -void SurfaceInterceptor::addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) { - sp<IBinder> asBinder = IInterface::asBinder(listener); - - std::scoped_lock lock(mListenersMutex); - - asBinder->linkToDeath(this); - - listener->onToggled(mEnabled); // notifies of current state - - mTraceToggledListeners.emplace(asBinder, listener); -} - -void SurfaceInterceptor::binderDied(const wp<IBinder>& who) { - std::scoped_lock lock(mListenersMutex); - mTraceToggledListeners.erase(who); -} - -void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers, - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays) -{ - if (mEnabled) { - return; - } - ATRACE_CALL(); - { - std::scoped_lock lock(mListenersMutex); - for (const auto& [_, listener] : mTraceToggledListeners) { - listener->onToggled(true); - } - } - mEnabled = true; - std::scoped_lock<std::mutex> protoGuard(mTraceMutex); - saveExistingDisplaysLocked(displays); - saveExistingSurfacesLocked(layers); -} - -void SurfaceInterceptor::disable() { - if (!mEnabled) { - return; - } - ATRACE_CALL(); - { - std::scoped_lock lock(mListenersMutex); - for (const auto& [_, listener] : mTraceToggledListeners) { - listener->onToggled(false); - } - } - mEnabled = false; - std::scoped_lock<std::mutex> protoGuard(mTraceMutex); - 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"); - mTrace.Clear(); -} - -bool SurfaceInterceptor::isEnabled() { - return mEnabled; -} - -void SurfaceInterceptor::saveExistingDisplaysLocked( - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays) -{ - // Caveat: The initial snapshot does not capture the power mode of the existing displays - ATRACE_CALL(); - for (size_t i = 0 ; i < displays.size() ; i++) { - addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]); - addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]); - } -} - -void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) { - ATRACE_CALL(); - for (const auto& l : layers) { - l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) { - addSurfaceCreationLocked(createTraceIncrementLocked(), layer); - addInitialSurfaceStateLocked(createTraceIncrementLocked(), layer); - }); - } -} - -void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, - const sp<const Layer>& layer) -{ - Transaction* transaction(increment->mutable_transaction()); - const uint32_t layerFlags = layer->getTransactionFlags(); - transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous); - transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation); - - const int32_t layerId(getLayerId(layer)); - addPositionLocked(transaction, layerId, layer->mDrawingState.transform.tx(), - layer->mDrawingState.transform.ty()); - addDepthLocked(transaction, layerId, layer->mDrawingState.z); - addAlphaLocked(transaction, layerId, layer->mDrawingState.color.a); - addTransparentRegionLocked(transaction, layerId, - layer->mDrawingState.activeTransparentRegion_legacy); - addLayerStackLocked(transaction, layerId, layer->mDrawingState.layerStack); - addCropLocked(transaction, layerId, layer->mDrawingState.crop); - addCornerRadiusLocked(transaction, layerId, layer->mDrawingState.cornerRadius); - addBackgroundBlurRadiusLocked(transaction, layerId, layer->mDrawingState.backgroundBlurRadius); - addBlurRegionsLocked(transaction, layerId, layer->mDrawingState.blurRegions); - addFlagsLocked(transaction, layerId, layer->mDrawingState.flags, - layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque | - layer_state_t::eLayerSecure); - addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mDrawingParent)); - addRelativeParentLocked(transaction, layerId, - getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf), - layer->mDrawingState.z); - addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius); - addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay); -} - -void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, - const DisplayDeviceState& display) -{ - Transaction* transaction(increment->mutable_transaction()); - transaction->set_synchronous(false); - transaction->set_animation(false); - - addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface); - addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack); - addDisplayFlagsLocked(transaction, display.sequenceId, display.flags); - addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height); - addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation), - display.layerStackSpaceRect, display.orientedDisplaySpaceRect); -} - -status_t SurfaceInterceptor::writeProtoFileLocked() { - ATRACE_CALL(); - std::string output; - - if (!mTrace.IsInitialized()) { - return NOT_ENOUGH_DATA; - } - if (!mTrace.SerializeToString(&output)) { - return PERMISSION_DENIED; - } - if (!android::base::WriteStringToFile(output, mOutputFileName, true)) { - return PERMISSION_DENIED; - } - - return NO_ERROR; -} - -const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const { - sp<IBinder> handle = weakHandle.promote(); - return Layer::fromHandle(handle).promote(); -} - -int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const { - return layer->sequence; -} - -int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const { - if (layer == nullptr) { - return -1; - } - auto strongLayer = layer.promote(); - return strongLayer == nullptr ? -1 : getLayerId(strongLayer); -} - -int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const { - if (handle == nullptr) { - return -1; - } - const sp<const Layer> layer = Layer::fromHandle(handle).promote(); - return layer == nullptr ? -1 : getLayerId(layer); -} - -Increment* SurfaceInterceptor::createTraceIncrementLocked() { - Increment* increment(mTrace.add_increment()); - increment->set_time_stamp(elapsedRealtimeNano()); - return increment; -} - -SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction, - int32_t layerId) -{ - SurfaceChange* change(transaction->add_surface_change()); - change->set_id(layerId); - return change; -} - -DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction, - int32_t sequenceId) -{ - DisplayChange* dispChange(transaction->add_display_change()); - dispChange->set_id(sequenceId); - return dispChange; -} - -void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) { - protoRect->set_left(rect.left); - protoRect->set_top(rect.top); - protoRect->set_right(rect.right); - protoRect->set_bottom(rect.bottom); -} - -void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid, - int32_t uid) { - Origin* origin(transaction->mutable_origin()); - origin->set_pid(pid); - origin->set_uid(uid); -} - -void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId, - float x, float y) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - PositionChange* posChange(change->mutable_position()); - posChange->set_x(x); - posChange->set_y(y); -} - -void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId, - uint32_t z) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - LayerChange* depthChange(change->mutable_layer()); - depthChange->set_layer(z); -} - -void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, - uint32_t h) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - SizeChange* sizeChange(change->mutable_size()); - sizeChange->set_w(w); - sizeChange->set_h(h); -} - -void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId, - float alpha) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - AlphaChange* alphaChange(change->mutable_alpha()); - alphaChange->set_alpha(alpha); -} - -void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId, - const layer_state_t::matrix22_t& matrix) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - MatrixChange* matrixChange(change->mutable_matrix()); - matrixChange->set_dsdx(matrix.dsdx); - matrixChange->set_dtdx(matrix.dtdx); - matrixChange->set_dsdy(matrix.dsdy); - matrixChange->set_dtdy(matrix.dtdy); -} - -void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction, - int32_t layerId, const Region& transRegion) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint()); - - for (const auto& rect : transRegion) { - Rectangle* protoRect(transparentChange->add_region()); - setProtoRectLocked(protoRect, rect); - } -} - -void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, - uint8_t mask) { - // There can be multiple flags changed - if (mask & layer_state_t::eLayerHidden) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - HiddenFlagChange* flagChange(change->mutable_hidden_flag()); - flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden); - } - if (mask & layer_state_t::eLayerOpaque) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - OpaqueFlagChange* flagChange(change->mutable_opaque_flag()); - flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque); - } - if (mask & layer_state_t::eLayerSecure) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - SecureFlagChange* flagChange(change->mutable_secure_flag()); - flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure); - } -} - -void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId, - ui::LayerStack layerStack) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - LayerStackChange* layerStackChange(change->mutable_layer_stack()); - layerStackChange->set_layer_stack(layerStack.id); -} - -void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId, - const Rect& rect) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - CropChange* cropChange(change->mutable_crop()); - Rectangle* protoRect(cropChange->mutable_rectangle()); - setProtoRectLocked(protoRect, rect); -} - -void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId, - float cornerRadius) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius()); - cornerRadiusChange->set_corner_radius(cornerRadius); -} - -void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, - int32_t backgroundBlurRadius) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius()); - blurRadiusChange->set_background_blur_radius(backgroundBlurRadius); -} - -void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId, - const std::vector<BlurRegion>& blurRegions) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions()); - for (const auto blurRegion : blurRegions) { - const auto blurRegionChange = blurRegionsChange->add_blur_regions(); - blurRegionChange->set_blur_radius(blurRegion.blurRadius); - blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL); - blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR); - blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL); - blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR); - blurRegionChange->set_alpha(blurRegion.alpha); - blurRegionChange->set_left(blurRegion.left); - blurRegionChange->set_top(blurRegion.top); - blurRegionChange->set_right(blurRegion.right); - blurRegionChange->set_bottom(blurRegion.bottom); - } -} - -void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId, - int32_t parentId) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - ReparentChange* overrideChange(change->mutable_reparent()); - overrideChange->set_parent_id(parentId); -} - -void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId, - int32_t parentId, int z) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - RelativeParentChange* overrideChange(change->mutable_relative_parent()); - overrideChange->set_relative_parent_id(parentId); - overrideChange->set_z(z); -} - -void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId, - float shadowRadius) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - ShadowRadiusChange* overrideChange(change->mutable_shadow_radius()); - overrideChange->set_radius(shadowRadius); -} - -void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, - bool isTrustedOverlay) { - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay()); - overrideChange->set_is_trusted_overlay(isTrustedOverlay); -} - -void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, - const layer_state_t& state) -{ - const sp<const Layer> layer(getLayer(state.surface)); - if (layer == nullptr) { - ALOGE("An existing layer could not be retrieved with the surface " - "from the layer_state_t surface in the update transaction"); - return; - } - - const int32_t layerId(getLayerId(layer)); - - if (state.what & layer_state_t::ePositionChanged) { - addPositionLocked(transaction, layerId, state.x, state.y); - } - if (state.what & layer_state_t::eLayerChanged) { - addDepthLocked(transaction, layerId, state.z); - } - if (state.what & layer_state_t::eSizeChanged) { - addSizeLocked(transaction, layerId, state.w, state.h); - } - if (state.what & layer_state_t::eAlphaChanged) { - addAlphaLocked(transaction, layerId, state.alpha); - } - if (state.what & layer_state_t::eMatrixChanged) { - addMatrixLocked(transaction, layerId, state.matrix); - } - if (state.what & layer_state_t::eTransparentRegionChanged) { - addTransparentRegionLocked(transaction, layerId, state.transparentRegion); - } - if (state.what & layer_state_t::eFlagsChanged) { - addFlagsLocked(transaction, layerId, state.flags, state.mask); - } - if (state.what & layer_state_t::eLayerStackChanged) { - addLayerStackLocked(transaction, layerId, state.layerStack); - } - if (state.what & layer_state_t::eCropChanged) { - addCropLocked(transaction, layerId, state.crop); - } - if (state.what & layer_state_t::eCornerRadiusChanged) { - addCornerRadiusLocked(transaction, layerId, state.cornerRadius); - } - if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) { - addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius); - } - if (state.what & layer_state_t::eBlurRegionsChanged) { - addBlurRegionsLocked(transaction, layerId, state.blurRegions); - } - if (state.what & layer_state_t::eReparent) { - auto parentHandle = (state.parentSurfaceControlForChild) - ? state.parentSurfaceControlForChild->getHandle() - : nullptr; - addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle)); - } - if (state.what & layer_state_t::eRelativeLayerChanged) { - addRelativeParentLocked(transaction, layerId, - getLayerIdFromHandle( - state.relativeLayerSurfaceControl->getHandle()), - state.z); - } - if (state.what & layer_state_t::eShadowRadiusChanged) { - addShadowRadiusLocked(transaction, layerId, state.shadowRadius); - } - if (state.what & layer_state_t::eTrustedOverlayChanged) { - addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay); - } - if (state.what & layer_state_t::eStretchChanged) { - ALOGW("SurfaceInterceptor not implemented for eStretchChanged"); - } -} - -void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, - const DisplayState& state, int32_t sequenceId) -{ - if (state.what & DisplayState::eSurfaceChanged) { - addDisplaySurfaceLocked(transaction, sequenceId, state.surface); - } - if (state.what & DisplayState::eLayerStackChanged) { - addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack); - } - if (state.what & DisplayState::eFlagsChanged) { - addDisplayFlagsLocked(transaction, sequenceId, state.flags); - } - if (state.what & DisplayState::eDisplaySizeChanged) { - addDisplaySizeLocked(transaction, sequenceId, state.width, state.height); - } - if (state.what & DisplayState::eDisplayProjectionChanged) { - addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation), - state.layerStackSpaceRect, state.orientedDisplaySpaceRect); - } -} - -void SurfaceInterceptor::addTransactionLocked( - Increment* increment, const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid, - int originUid, uint64_t transactionId) { - Transaction* transaction(increment->mutable_transaction()); - transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous); - transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation); - setTransactionOriginLocked(transaction, originPid, originUid); - transaction->set_id(transactionId); - for (const auto& compState: stateUpdates) { - addSurfaceChangesLocked(transaction, compState.state); - } - for (const auto& disp: changedDisplays) { - ssize_t dpyIdx = displays.indexOfKey(disp.token); - if (dpyIdx >= 0) { - const DisplayDeviceState& dispState(displays.valueAt(dpyIdx)); - addDisplayChangesLocked(transaction, disp, dispState.sequenceId); - } - } -} - -void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment, - const sp<const Layer>& layer) -{ - SurfaceCreation* creation(increment->mutable_surface_creation()); - creation->set_id(getLayerId(layer)); - creation->set_name(layer->getName()); - creation->set_w(layer->mDrawingState.active_legacy.w); - creation->set_h(layer->mDrawingState.active_legacy.h); -} - -void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment, - const sp<const Layer>& layer) -{ - SurfaceDeletion* deletion(increment->mutable_surface_deletion()); - deletion->set_id(getLayerId(layer)); -} - -void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId, - uint32_t width, uint32_t height, uint64_t frameNumber) -{ - BufferUpdate* update(increment->mutable_buffer_update()); - update->set_id(layerId); - update->set_w(width); - update->set_h(height); - update->set_frame_number(frameNumber); -} - -void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) { - VSyncEvent* event(increment->mutable_vsync_event()); - event->set_when(timestamp); -} - -void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId, - const sp<const IGraphicBufferProducer>& surface) -{ - if (surface == nullptr) { - return; - } - uint64_t bufferQueueId = 0; - status_t err(surface->getUniqueId(&bufferQueueId)); - if (err == NO_ERROR) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); - DispSurfaceChange* surfaceChange(dispChange->mutable_surface()); - surfaceChange->set_buffer_queue_id(bufferQueueId); - surfaceChange->set_buffer_queue_name(surface->getConsumerName().string()); - } - else { - ALOGE("invalid graphic buffer producer received while tracing a display change (%s)", - strerror(-err)); - } -} - -void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, - ui::LayerStack layerStack) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); - LayerStackChange* layerStackChange(dispChange->mutable_layer_stack()); - layerStackChange->set_layer_stack(layerStack.id); -} - -void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, - uint32_t flags) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); - DisplayFlagsChange* flagsChange(dispChange->mutable_flags()); - flagsChange->set_flags(flags); -} - -void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, - uint32_t w, uint32_t h) -{ - DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); - SizeChange* sizeChange(dispChange->mutable_size()); - sizeChange->set_w(w); - sizeChange->set_h(h); -} - -void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction, - int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame) -{ - DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); - ProjectionChange* projectionChange(dispChange->mutable_projection()); - projectionChange->set_orientation(orientation); - Rectangle* viewportRect(projectionChange->mutable_viewport()); - setProtoRectLocked(viewportRect, viewport); - Rectangle* frameRect(projectionChange->mutable_frame()); - setProtoRectLocked(frameRect, frame); -} - -void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment, - const DisplayDeviceState& info) -{ - DisplayCreation* creation(increment->mutable_display_creation()); - creation->set_id(info.sequenceId); - creation->set_name(info.displayName); - creation->set_is_secure(info.isSecure); - if (info.physical) { - creation->set_display_id(info.physical->id.value); - } -} - -void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) { - DisplayDeletion* deletion(increment->mutable_display_deletion()); - deletion->set_id(sequenceId); -} - -void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, - int32_t mode) -{ - PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update()); - powerModeUpdate->set_id(sequenceId); - powerModeUpdate->set_mode(mode); -} - -void SurfaceInterceptor::saveTransaction( - const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid, - uint64_t transactionId) { - if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays, - flags, originPid, originUid, transactionId); -} - -void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) { - if (!mEnabled || layer == nullptr) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addSurfaceCreationLocked(createTraceIncrementLocked(), layer); -} - -void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) { - if (!mEnabled || layer == nullptr) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addSurfaceDeletionLocked(createTraceIncrementLocked(), layer); -} - -/** - * Here we pass the layer by ID instead of by sp<> since this is called without - * holding the state-lock from a Binder thread. If we required the caller - * to pass 'this' by sp<> the temporary sp<> constructed could end up - * being the last reference and we might accidentally destroy the Layer - * from this binder thread. - */ -void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width, - uint32_t height, uint64_t frameNumber) -{ - if (!mEnabled) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber); -} - -void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) { - if (!mEnabled) { - return; - } - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp); -} - -void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) { - if (!mEnabled) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addDisplayCreationLocked(createTraceIncrementLocked(), info); -} - -void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) { - if (!mEnabled) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId); -} - -void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) { - if (!mEnabled) { - return; - } - ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode); -} - -} // namespace impl -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h deleted file mode 100644 index 970c3e5c27..0000000000 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2016 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_SURFACEINTERCEPTOR_H -#define ANDROID_SURFACEINTERCEPTOR_H - -#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h> - -#include <mutex> - -#include <binder/IBinder.h> - -#include <gui/LayerState.h> - -#include <utils/KeyedVector.h> -#include <utils/SortedVector.h> -#include <utils/StrongPointer.h> -#include <utils/Vector.h> - -#include "DisplayDevice.h" - -namespace android { - -class BufferItem; -class Layer; -class SurfaceFlinger; -struct ComposerState; -struct DisplayDeviceState; -struct DisplayState; -struct layer_state_t; -using Transaction = surfaceflinger::Transaction; -using Trace = surfaceflinger::Trace; -using Rectangle = surfaceflinger::Rectangle; -using SurfaceChange = surfaceflinger::SurfaceChange; -using Increment = surfaceflinger::Increment; -using DisplayChange = surfaceflinger::DisplayChange; - -constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope"; - -class SurfaceInterceptor : public IBinder::DeathRecipient { -public: - virtual ~SurfaceInterceptor(); - - // Both vectors are used to capture the current state of SF as the initial snapshot in the trace - virtual void enable(const SortedVector<sp<Layer>>& layers, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0; - virtual void disable() = 0; - virtual bool isEnabled() = 0; - - virtual void addTransactionTraceListener( - const sp<gui::ITransactionTraceListener>& listener) = 0; - virtual void binderDied(const wp<IBinder>& who) = 0; - - // Intercept display and surface transactions - virtual void saveTransaction( - const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, - int originUid, uint64_t transactionId) = 0; - - // Intercept surface data - virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0; - virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0; - virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height, - uint64_t frameNumber) = 0; - - // Intercept display data - virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0; - virtual void saveDisplayDeletion(int32_t sequenceId) = 0; - virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0; - virtual void saveVSyncEvent(nsecs_t timestamp) = 0; -}; - -namespace impl { - -/* - * SurfaceInterceptor intercepts and stores incoming streams of window - * properties on SurfaceFlinger. - */ -class SurfaceInterceptor final : public android::SurfaceInterceptor { -public: - SurfaceInterceptor() = default; - ~SurfaceInterceptor() override = default; - - // Both vectors are used to capture the current state of SF as the initial snapshot in the trace - void enable(const SortedVector<sp<Layer>>& layers, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override; - void disable() override; - bool isEnabled() override; - - void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override; - void binderDied(const wp<IBinder>& who) override; - - // Intercept display and surface transactions - void saveTransaction(const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, - int originUid, uint64_t transactionId) override; - - // Intercept surface data - void saveSurfaceCreation(const sp<const Layer>& layer) override; - void saveSurfaceDeletion(const sp<const Layer>& layer) override; - void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height, - uint64_t frameNumber) override; - - // Intercept display data - void saveDisplayCreation(const DisplayDeviceState& info) override; - void saveDisplayDeletion(int32_t sequenceId) override; - void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override; - void saveVSyncEvent(nsecs_t timestamp) override; - -private: - // The creation increments of Surfaces and Displays do not contain enough information to capture - // the initial state of each object, so a transaction with all of the missing properties is - // performed at the initial snapshot for each display and surface. - void saveExistingDisplaysLocked( - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays); - void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers); - void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer); - void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display); - - status_t writeProtoFileLocked(); - const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const; - int32_t getLayerId(const sp<const Layer>& layer) const; - int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const; - int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const; - - Increment* createTraceIncrementLocked(); - void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer); - void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer); - void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width, - uint32_t height, uint64_t frameNumber); - void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp); - void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info); - void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId); - void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode); - - // Add surface transactions to the trace - SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId); - void setProtoRectLocked(Rectangle* protoRect, const Rect& rect); - void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y); - void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z); - void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h); - void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha); - void addMatrixLocked(Transaction* transaction, int32_t layerId, - const layer_state_t::matrix22_t& matrix); - void addTransparentRegionLocked(Transaction* transaction, int32_t layerId, - const Region& transRegion); - void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask); - void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack); - void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); - void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); - void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, - int32_t backgroundBlurRadius); - void addBlurRegionsLocked(Transaction* transaction, int32_t layerId, - const std::vector<BlurRegion>& effectRegions); - void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state); - void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, - uint32_t transactionFlags, int originPid, int originUid, - uint64_t transactionId); - void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId); - void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId, - int z); - void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius); - void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay); - - // Add display transactions to the trace - DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId); - void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId, - const sp<const IGraphicBufferProducer>& surface); - void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack); - void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags); - void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, - uint32_t h); - void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId, - int32_t orientation, const Rect& viewport, const Rect& frame); - void addDisplayChangesLocked(Transaction* transaction, - const DisplayState& state, int32_t sequenceId); - - // Add transaction origin to trace - void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid); - - bool mEnabled {false}; - std::string mOutputFileName {DEFAULT_FILENAME}; - std::mutex mTraceMutex {}; - Trace mTrace {}; - std::mutex mListenersMutex; - std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners - GUARDED_BY(mListenersMutex); -}; - -} // namespace impl - -} // namespace android - -#endif // ANDROID_SURFACEINTERCEPTOR_H diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index e5a9dd47c3..630cef1fd4 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -27,6 +27,7 @@ #include <algorithm> #include <chrono> +#include <cmath> #include <unordered_map> #include "TimeStats.h" @@ -68,6 +69,8 @@ SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) { return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE; case GameMode::Battery: return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY; + case GameMode::Custom: + return SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM; default: return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED; } @@ -88,7 +91,7 @@ SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto( } } // namespace -bool TimeStats::populateGlobalAtom(std::string* pulledData) { +bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) { std::lock_guard<std::mutex> lock(mMutex); if (mTimeStats.statsStartLegacy == 0) { @@ -136,10 +139,11 @@ bool TimeStats::populateGlobalAtom(std::string* pulledData) { // Always clear data. clearGlobalLocked(); - return atomList.SerializeToString(pulledData); + pulledData->resize(atomList.ByteSizeLong()); + return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong()); } -bool TimeStats::populateLayerAtom(std::string* pulledData) { +bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) { std::lock_guard<std::mutex> lock(mMutex); std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats; @@ -177,6 +181,12 @@ bool TimeStats::populateLayerAtom(std::string* pulledData) { *atom->mutable_present_to_present() = histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets); } + const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta"); + if (present2PresentDeltaHist != layer->deltas.cend()) { + *atom->mutable_present_to_present_delta() = + histogramToProto(present2PresentDeltaHist->second.hist, + mMaxPulledHistogramBuckets); + } const auto& post2presentHist = layer->deltas.find("post2present"); if (post2presentHist != layer->deltas.cend()) { *atom->mutable_post_to_present() = @@ -227,7 +237,8 @@ bool TimeStats::populateLayerAtom(std::string* pulledData) { // Always clear data. clearLayersLocked(); - return atomList.SerializeToString(pulledData); + pulledData->resize(atomList.ByteSizeLong()); + return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong()); } TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {} @@ -243,7 +254,7 @@ TimeStats::TimeStats(std::optional<size_t> maxPulledLayers, } } -bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) { +bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) { bool success = false; if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO success = populateGlobalAtom(pulledData); @@ -448,6 +459,7 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR LayerRecord& layerRecord = mTimeStatsTracker[layerId]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; + std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; const int32_t refreshRateBucket = clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); @@ -525,6 +537,12 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId, timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); + if (prevPresentToPresentMs) { + const int32_t presentToPresentDeltaMs = + std::abs(presentToPresentMs - *prevPresentToPresentMs); + timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs); + } + prevPresentToPresentMs = presentToPresentMs; } prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 7a159b8eb7..5f586577ac 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -34,6 +34,8 @@ #include <scheduler/Fps.h> +using android::gui::GameMode; +using android::gui::LayerMetadata; using namespace android::surfaceflinger; namespace android { @@ -45,7 +47,7 @@ public: virtual ~TimeStats() = default; // Process a pull request from statsd. - virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0; + virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0; virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0; virtual bool isEnabled() = 0; @@ -217,6 +219,7 @@ class TimeStats : public android::TimeStats { uint32_t lateAcquireFrames = 0; uint32_t badDesiredPresentFrames = 0; TimeRecord prevTimeRecord; + std::optional<int32_t> prevPresentToPresentMs; std::deque<TimeRecord> timeRecords; }; @@ -242,7 +245,7 @@ public: TimeStats(std::optional<size_t> maxPulledLayers, std::optional<size_t> maxPulledHistogramBuckets); - bool onPullAtom(const int atomId, std::string* pulledData) override; + bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override; void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override; bool isEnabled() override; std::string miniDump() override; @@ -290,8 +293,8 @@ public: static const size_t MAX_NUM_TIME_RECORDS = 64; private: - bool populateGlobalAtom(std::string* pulledData); - bool populateLayerAtom(std::string* pulledData); + bool populateGlobalAtom(std::vector<uint8_t>* pulledData); + bool populateLayerAtom(std::vector<uint8_t>* pulledData); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote, diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto index e45757ddfd..8615947db5 100644 --- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto +++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto @@ -173,6 +173,7 @@ message SurfaceflingerStatsLayerInfo { GAME_MODE_STANDARD = 2; GAME_MODE_PERFORMANCE = 3; GAME_MODE_BATTERY = 4; + GAME_MODE_CUSTOM = 5; } // Game mode that the layer was running at. Used to track user engagement @@ -288,7 +289,11 @@ message SurfaceflingerStatsLayerInfo { // Introduced in Android 12. optional FrameTimingHistogram app_deadline_misses = 25; - // Next ID: 27 + // Variability histogram of present_to_present timings. + // Introduced in Android 14. + optional FrameTimingHistogram present_to_present_delta = 27; + + // Next ID: 28 } /** diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 237ae8d761..60aa810e8b 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -24,6 +24,9 @@ #include <unordered_map> #include <vector> +using android::gui::GameMode; +using android::gui::LayerMetadata; + namespace android { namespace surfaceflinger { diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index 49554c7dd8..566d55353f 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -89,6 +89,9 @@ LayersTraceFileProto LayerTracing::createTraceFileProto() const { LayersTraceFileProto fileProto; fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) - + systemTime(SYSTEM_TIME_MONOTONIC)); + fileProto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs); return fileProto; } @@ -98,7 +101,7 @@ void LayerTracing::dump(std::string& result) const { mBuffer->dump(result); } -void LayerTracing::notify(bool visibleRegionDirty, int64_t time) { +void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId) { std::scoped_lock lock(mTraceLock); if (!mEnabled) { return; @@ -129,6 +132,7 @@ void LayerTracing::notify(bool visibleRegionDirty, int64_t time) { entry.set_excludes_composition_state(true); } mFlinger.dumpDisplayProto(entry); + entry.set_vsync_id(vsyncId); mBuffer->emplace(std::move(entry)); } diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h index 88a19ecdf1..b32001cc63 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.h +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -47,7 +47,7 @@ public: bool isEnabled() const; status_t writeToFile(); LayersTraceFileProto createTraceFileProto() const; - void notify(bool visibleRegionDirty, int64_t time); + void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId); enum : uint32_t { TRACE_INPUT = 1 << 1, @@ -55,6 +55,7 @@ public: TRACE_EXTRA = 1 << 3, TRACE_HWC = 1 << 4, TRACE_BUFFERS = 1 << 5, + TRACE_VIRTUAL_DISPLAYS = 1 << 6, TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA, }; void setTraceFlags(uint32_t flags); diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index a73eccf0e5..2f464873ea 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -15,6 +15,7 @@ */ #include <gui/SurfaceComposerClient.h> +#include <ui/Fence.h> #include <ui/Rect.h> #include "LayerProtoHelper.h" @@ -87,10 +88,7 @@ proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) { if (layer.what & layer_state_t::eLayerChanged) { proto.set_z(layer.z); } - if (layer.what & layer_state_t::eSizeChanged) { - proto.set_w(layer.w); - proto.set_h(layer.h); - } + if (layer.what & layer_state_t::eLayerStackChanged) { proto.set_layer_stack(layer.layerStack.id); } @@ -113,7 +111,7 @@ proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) { } if (layer.what & layer_state_t::eAlphaChanged) { - proto.set_alpha(layer.alpha); + proto.set_alpha(layer.color.a); } if (layer.what & layer_state_t::eColorChanged) { @@ -125,8 +123,8 @@ proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) { if (layer.what & layer_state_t::eTransparentRegionChanged) { LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region()); } - if (layer.what & layer_state_t::eTransformChanged) { - proto.set_transform(layer.transform); + if (layer.what & layer_state_t::eBufferTransformChanged) { + proto.set_transform(layer.bufferTransform); } if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) { proto.set_transform_to_display_inverse(layer.transformToDisplayInverse); @@ -312,10 +310,10 @@ TransactionState TransactionProtoParser::fromProto(const proto::TransactionState int32_t layerCount = proto.layer_changes_size(); t.states.reserve(static_cast<size_t>(layerCount)); for (int i = 0; i < layerCount; i++) { - ComposerState s; + ResolvedComposerState s; s.state.what = 0; fromProto(proto.layer_changes(i), s.state); - t.states.add(s); + t.states.emplace_back(s); } int32_t displayCount = proto.display_changes_size(); @@ -375,10 +373,6 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta if (proto.what() & layer_state_t::eLayerChanged) { layer.z = proto.z(); } - if (proto.what() & layer_state_t::eSizeChanged) { - layer.w = proto.w(); - layer.h = proto.h(); - } if (proto.what() & layer_state_t::eLayerStackChanged) { layer.layerStack.id = proto.layer_stack(); } @@ -401,7 +395,7 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta } if (proto.what() & layer_state_t::eAlphaChanged) { - layer.alpha = proto.alpha(); + layer.color.a = proto.alpha(); } if (proto.what() & layer_state_t::eColorChanged) { @@ -413,8 +407,8 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta if (proto.what() & layer_state_t::eTransparentRegionChanged) { LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion); } - if (proto.what() & layer_state_t::eTransformChanged) { - layer.transform = proto.transform(); + if (proto.what() & layer_state_t::eBufferTransformChanged) { + layer.bufferTransform = proto.transform(); } if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) { layer.transformToDisplayInverse = proto.transform_to_display_inverse(); @@ -456,9 +450,9 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta layer.parentSurfaceControlForChild = nullptr; } else { layer.parentSurfaceControlForChild = - new SurfaceControl(SurfaceComposerClient::getDefault(), - mMapper->getLayerHandle(static_cast<int32_t>(layerId)), - nullptr, static_cast<int32_t>(layerId)); + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + mMapper->getLayerHandle(static_cast<int32_t>(layerId)), + static_cast<int32_t>(layerId), ""); } } if (proto.what() & layer_state_t::eRelativeLayerChanged) { @@ -467,9 +461,9 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta layer.relativeLayerSurfaceControl = nullptr; } else { layer.relativeLayerSurfaceControl = - new SurfaceControl(SurfaceComposerClient::getDefault(), - mMapper->getLayerHandle(static_cast<int32_t>(layerId)), - nullptr, static_cast<int32_t>(layerId)); + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + mMapper->getLayerHandle(static_cast<int32_t>(layerId)), + static_cast<int32_t>(layerId), ""); } layer.z = proto.z(); } @@ -497,8 +491,13 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta inputInfo.replaceTouchableRegionWithCrop = windowInfoProto.replace_touchable_region_with_crop(); int64_t layerId = windowInfoProto.crop_layer_id(); - inputInfo.touchableRegionCropHandle = - mMapper->getLayerHandle(static_cast<int32_t>(layerId)); + if (layerId != -1) { + inputInfo.touchableRegionCropHandle = + mMapper->getLayerHandle(static_cast<int32_t>(layerId)); + } else { + inputInfo.touchableRegionCropHandle = wp<IBinder>(); + } + layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo); } if (proto.what() & layer_state_t::eBackgroundColorChanged) { diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h index 872a901b21..2232bb9cb1 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.h +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h @@ -15,6 +15,7 @@ */ #pragma once +#include <gui/fake/BufferData.h> #include <layerproto/TransactionProto.h> #include <utils/RefBase.h> @@ -43,35 +44,6 @@ struct TracingLayerState : layer_state_t { TracingLayerCreationArgs args; }; -// Class which exposes buffer properties from BufferData without holding on to the actual buffer -// handle. -class BufferDataStub : public BufferData { -public: - BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat, - uint64_t outUsage) - : mBufferId(bufferId), - mWidth(width), - mHeight(height), - mPixelFormat(pixelFormat), - mOutUsage(outUsage) {} - bool hasBuffer() const override { return mBufferId != 0; } - bool hasSameBuffer(const BufferData& other) const override { - return getId() == other.getId() && frameNumber == other.frameNumber; - } - uint32_t getWidth() const override { return mWidth; } - uint32_t getHeight() const override { return mHeight; } - uint64_t getId() const override { return mBufferId; } - PixelFormat getPixelFormat() const override { return mPixelFormat; } - uint64_t getUsage() const override { return mOutUsage; } - -private: - uint64_t mBufferId; - uint32_t mWidth; - uint32_t mHeight; - int32_t mPixelFormat; - uint64_t mOutUsage; -}; - class TransactionProtoParser { public: // Utility class to map handles to ids and buffers to buffer properties without pulling @@ -87,7 +59,7 @@ public: virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat, uint64_t usage) const { - return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage); + return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage); } virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */, uint64_t* /* outBufferId */, diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp index 6381758c7b..cb5320b88c 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.cpp +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -134,6 +134,9 @@ proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const { proto::TransactionTraceFile proto; proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 | proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L); + auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) - + systemTime(SYSTEM_TIME_MONOTONIC)); + proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs); return proto; } @@ -152,17 +155,33 @@ void TransactionTracing::addQueuedTransaction(const TransactionState& transactio mTransactionQueue.push(state); } -void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions, - int64_t vsyncId) { +TransactionTracing::CommittedTransactions& +TransactionTracing::findOrCreateCommittedTransactionRecord(int64_t vsyncId) { + for (auto& pendingTransaction : mPendingTransactions) { + if (pendingTransaction.vsyncId == vsyncId) { + return pendingTransaction; + } + } + CommittedTransactions committedTransactions; committedTransactions.vsyncId = vsyncId; committedTransactions.timestamp = systemTime(); + mPendingTransactions.emplace_back(committedTransactions); + return mPendingTransactions.back(); +} + +void TransactionTracing::onLayerAddedToDrawingState(int layerId, int64_t vsyncId) { + CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId); + committedTransactions.createdLayerIds.emplace_back(layerId); +} + +void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions, + int64_t vsyncId) { + CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId); committedTransactions.transactionIds.reserve(transactions.size()); for (const auto& transaction : transactions) { committedTransactions.transactionIds.emplace_back(transaction.id); } - - mPendingTransactions.emplace_back(committedTransactions); tryPushToTracingThread(); } @@ -235,15 +254,24 @@ void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& comm for (const CommittedTransactions& entry : committedTransactions) { entryProto.set_elapsed_realtime_nanos(entry.timestamp); entryProto.set_vsync_id(entry.vsyncId); - entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size())); - for (auto& newLayer : mCreatedLayers) { - entryProto.mutable_added_layers()->Add(std::move(newLayer)); + entryProto.mutable_added_layers()->Reserve( + static_cast<int32_t>(entry.createdLayerIds.size())); + + for (const int32_t& id : entry.createdLayerIds) { + auto it = mCreatedLayers.find(id); + if (it != mCreatedLayers.end()) { + entryProto.mutable_added_layers()->Add(std::move(it->second)); + mCreatedLayers.erase(it); + } else { + ALOGW("Could not created layer with id %d", id); + } } + entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size())); for (auto& removedLayer : removedLayers) { entryProto.mutable_removed_layers()->Add(removedLayer); + mCreatedLayers.erase(removedLayer); } - mCreatedLayers.clear(); entryProto.mutable_transactions()->Reserve( static_cast<int32_t>(entry.transactionIds.size())); for (const uint64_t& id : entry.transactionIds) { @@ -256,6 +284,14 @@ void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& comm } } + entryProto.mutable_removed_layer_handles()->Reserve( + static_cast<int32_t>(mRemovedLayerHandles.size())); + for (auto& [handle, layerId] : mRemovedLayerHandles) { + entryProto.mutable_removed_layer_handles()->Add(layerId); + mLayerHandles.erase(handle); + } + mRemovedLayerHandles.clear(); + std::string serializedProto; entryProto.SerializeToString(&serializedProto); entryProto.Clear(); @@ -263,13 +299,6 @@ void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& comm removedEntries.reserve(removedEntries.size() + entries.size()); removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()), std::make_move_iterator(entries.end())); - - entryProto.mutable_removed_layer_handles()->Reserve( - static_cast<int32_t>(mRemovedLayerHandles.size())); - for (auto& handle : mRemovedLayerHandles) { - entryProto.mutable_removed_layer_handles()->Add(handle); - } - mRemovedLayerHandles.clear(); } proto::TransactionTraceEntry removedEntryProto; @@ -304,7 +333,7 @@ void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const s ALOGW("Duplicate handles found. %p", layerHandle); } mLayerHandles[layerHandle] = layerId; - mCreatedLayers.push_back(mProtoParser.toProto(args)); + mCreatedLayers[layerId] = mProtoParser.toProto(args); } void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId, @@ -315,7 +344,7 @@ void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId, ALOGW("Duplicate handles found. %p", layerHandle); } mLayerHandles[layerHandle] = layerId; - mCreatedLayers.emplace_back(mProtoParser.toProto(args)); + mCreatedLayers[layerId] = mProtoParser.toProto(args); } void TransactionTracing::onLayerRemoved(int32_t layerId) { @@ -330,9 +359,7 @@ void TransactionTracing::onHandleRemoved(BBinder* layerHandle) { ALOGW("handle not found. %p", layerHandle); return; } - - mRemovedLayerHandles.push_back(it->second); - mLayerHandles.erase(it); + mRemovedLayerHandles.emplace_back(layerHandle, it->second); } void TransactionTracing::tryPushToTracingThread() { @@ -376,10 +403,15 @@ void TransactionTracing::updateStartingStateLocked( } } + for (const int32_t removedLayerHandleId : removedEntry.removed_layer_handles()) { + mRemovedLayerHandlesAtStart.insert(removedLayerHandleId); + } + // Clean up stale starting states since the layer has been removed and the buffer does not // contain any references to the layer. for (const int32_t removedLayerId : removedEntry.removed_layers()) { mStartingStates.erase(removedLayerId); + mRemovedLayerHandlesAtStart.erase(removedLayerId); } } @@ -401,6 +433,12 @@ void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFi transactionProto.set_vsync_id(0); transactionProto.set_post_time(mStartingTimestamp); entryProto->mutable_transactions()->Add(std::move(transactionProto)); + + entryProto->mutable_removed_layer_handles()->Reserve( + static_cast<int32_t>(mRemovedLayerHandlesAtStart.size())); + for (const int32_t removedLayerHandleId : mRemovedLayerHandlesAtStart) { + entryProto->mutable_removed_layer_handles()->Add(removedLayerHandleId); + } } proto::TransactionTraceFile TransactionTracing::writeToProto() { diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h index 4c291f960f..ae01d3c0cc 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -64,6 +64,7 @@ public: int mirrorFromId); void onLayerRemoved(int layerId); void onHandleRemoved(BBinder* layerHandle); + void onLayerAddedToDrawingState(int layerId, int64_t vsyncId); void dump(std::string&) const; static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024; static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024; @@ -81,11 +82,13 @@ private: GUARDED_BY(mTraceLock); LocklessStack<proto::TransactionState> mTransactionQueue; nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock); - std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock); + std::unordered_map<int, proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock); std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles GUARDED_BY(mTraceLock); - std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock); + std::vector<std::pair<BBinder* /* layerHandle */, int32_t /* layerId */>> mRemovedLayerHandles + GUARDED_BY(mTraceLock); std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock); + std::set<int32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock); TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock); // Parses the transaction to proto without holding any tracing locks so we can generate proto // in the binder thread without any contention. @@ -100,6 +103,7 @@ private: std::condition_variable mTransactionsAddedToBufferCv; struct CommittedTransactions { std::vector<uint64_t> transactionIds; + std::vector<int32_t> createdLayerIds; int64_t vsyncId; int64_t timestamp; }; @@ -117,7 +121,7 @@ private: void tryPushToTracingThread() EXCLUDES(mMainThreadLock); void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock); void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock); - + CommittedTransactions& findOrCreateCommittedTransactionRecord(int64_t vsyncId); // TEST // Wait until all the committed transactions for the specified vsync id are added to the buffer. void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock); diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index cf44effe50..f1a6c0e2fa 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -16,6 +16,7 @@ #undef LOG_TAG #define LOG_TAG "LayerTraceGenerator" +//#define LOG_NDEBUG 0 #include <TestableSurfaceFlinger.h> #include <Tracing/TransactionProtoParser.h> @@ -46,43 +47,26 @@ public: return std::make_unique<scheduler::FakePhaseOffsets>(); } - sp<SurfaceInterceptor> createSurfaceInterceptor() override { - return new android::impl::SurfaceInterceptor(); - } - sp<StartPropertySetThread> createStartPropertySetThread( bool /* timestampPropertyValue */) override { - return nullptr; + return sp<StartPropertySetThread>(); } sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& /* creationArgs */) override { - return nullptr; + return sp<DisplayDevice>(); } sp<GraphicBuffer> createGraphicBuffer(uint32_t /* width */, uint32_t /* height */, PixelFormat /* format */, uint32_t /* layerCount */, uint64_t /* usage */, std::string /* requestorName */) override { - return nullptr; + return sp<GraphicBuffer>(); } void createBufferQueue(sp<IGraphicBufferProducer>* /* outProducer */, sp<IGraphicBufferConsumer>* /* outConsumer */, bool /* consumerIsSurfaceFlinger */) override {} - sp<IGraphicBufferProducer> createMonitoredProducer( - const sp<IGraphicBufferProducer>& /* producer */, - const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override { - return nullptr; - } - - sp<BufferLayerConsumer> createBufferLayerConsumer( - const sp<IGraphicBufferConsumer>& /* consumer */, - renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */, - Layer* /* layer */) override { - return nullptr; - } - std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( const sp<IGraphicBufferProducer>& /* producer */) override { return nullptr; @@ -92,21 +76,13 @@ public: return compositionengine::impl::createCompositionEngine(); } - sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) { - return sp<ContainerLayer>::make(args); + sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) { + return sp<Layer>::make(args); } - sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) { - return new BufferStateLayer(args); - } + sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); } - sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) { - return new EffectLayer(args); - } - - sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override { - return nullptr; - } + sp<LayerFE> createLayerFE(const std::string& layerName) { return sp<LayerFE>::make(layerName); } std::unique_ptr<FrameTracer> createFrameTracer() override { return std::make_unique<testing::NiceMock<mock::FrameTracer>>(); @@ -124,7 +100,8 @@ public: MockSurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {} std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData( - const BufferData& bufferData, const char* /* layerName */) const override { + BufferData& bufferData, const char* /* layerName */, + uint64_t /* transactionId */) override { return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(), bufferData.getHeight(), bufferData.getId(), @@ -142,6 +119,14 @@ public: transact(1033, data, &reply, 0 /* flags */); } + void setLayerTraceSize(int32_t sizeInKb) { + Parcel data; + Parcel reply; + data.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); + data.writeInt32(sizeInKb); + transact(1029, data, &reply, 0 /* flags */); + } + void startLayerTracing(int64_t traceStartTime) { Parcel data; Parcel reply; @@ -181,11 +166,12 @@ public: bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, const char* outputLayersTracePath) { if (traceFile.entry_size() == 0) { + ALOGD("Trace file is empty"); return false; } Factory mFactory; - sp<MockSurfaceFlinger> flinger = new MockSurfaceFlinger(mFactory); + sp<MockSurfaceFlinger> flinger = sp<MockSurfaceFlinger>::make(mFactory); TestableSurfaceFlinger mFlinger(flinger); mFlinger.setupRenderEngine( std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>()); @@ -205,32 +191,31 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, mFlinger.mutableMaxRenderTargetSize() = 16384; flinger->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS); + flinger->setLayerTraceSize(512 * 1024); // 512MB buffer size flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos()); std::unique_ptr<TraceGenFlingerDataMapper> mapper = std::make_unique<TraceGenFlingerDataMapper>(); TraceGenFlingerDataMapper* dataMapper = mapper.get(); TransactionProtoParser parser(std::move(mapper)); - nsecs_t frameTime; - int64_t vsyncId; ALOGD("Generating %d transactions...", traceFile.entry_size()); for (int i = 0; i < traceFile.entry_size(); i++) { proto::TransactionTraceEntry entry = traceFile.entry(i); ALOGV(" Entry %04d/%04d for time=%" PRId64 " vsyncid=%" PRId64 - " layers +%d -%d transactions=%d", + " layers +%d -%d handles -%d transactions=%d", i, traceFile.entry_size(), entry.elapsed_realtime_nanos(), entry.vsync_id(), - entry.added_layers_size(), entry.removed_layers_size(), entry.transactions_size()); + entry.added_layers_size(), entry.removed_layers_size(), + entry.removed_layer_handles_size(), entry.transactions_size()); for (int j = 0; j < entry.added_layers_size(); j++) { // create layers TracingLayerCreationArgs tracingArgs; parser.fromProto(entry.added_layers(j), tracingArgs); - sp<IBinder> outHandle; - int32_t outLayerId; + gui::CreateSurfaceResult outResult; LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name, - tracingArgs.flags, LayerMetadata()); - args.sequence = std::make_optional<int32_t>(tracingArgs.layerId); + tracingArgs.flags, LayerMetadata(), + std::make_optional<int32_t>(tracingArgs.layerId)); if (tracingArgs.mirrorFromId == -1) { sp<IBinder> parentHandle = nullptr; @@ -238,40 +223,33 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, (dataMapper->mLayerHandles.find(tracingArgs.parentId) == dataMapper->mLayerHandles.end())) { args.addToRoot = false; - } else { + } else if (tracingArgs.parentId != -1) { parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId); } - mFlinger.createLayer(args, &outHandle, parentHandle, &outLayerId, - nullptr /* parentLayer */, nullptr /* outTransformHint */); + mFlinger.createLayer(args, parentHandle, outResult); } else { sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId); - mFlinger.mirrorLayer(args, mirrorFromHandle, &outHandle, &outLayerId); + mFlinger.mirrorLayer(args, mirrorFromHandle, outResult); } - LOG_ALWAYS_FATAL_IF(outLayerId != tracingArgs.layerId, + LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId, "Could not create layer expected:%d actual:%d", tracingArgs.layerId, - outLayerId); - dataMapper->mLayerHandles[tracingArgs.layerId] = outHandle; + outResult.layerId); + dataMapper->mLayerHandles[tracingArgs.layerId] = outResult.handle; } for (int j = 0; j < entry.transactions_size(); j++) { // apply transactions TransactionState transaction = parser.fromProto(entry.transactions(j)); - mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, - transaction.displays, transaction.flags, - transaction.applyToken, transaction.inputWindowCommands, - transaction.desiredPresentTime, - transaction.isAutoTimestamp, {}, - transaction.hasListenerCallbacks, - transaction.listenerCallbacks, transaction.id); + mFlinger.setTransactionStateInternal(transaction); } + const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos()); + const auto vsyncId = VsyncId{entry.vsync_id()}; + mFlinger.commit(frameTime, vsyncId); + for (int j = 0; j < entry.removed_layer_handles_size(); j++) { dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j)); } - - frameTime = entry.elapsed_realtime_nanos(); - vsyncId = entry.vsync_id(); - mFlinger.commit(frameTime, vsyncId); } flinger->stopLayerTracing(outputLayersTracePath); @@ -280,4 +258,4 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, return true; } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp index f3cf42d7ab..9f9ae4815b 100644 --- a/services/surfaceflinger/Tracing/tools/main.cpp +++ b/services/surfaceflinger/Tracing/tools/main.cpp @@ -52,6 +52,10 @@ int main(int argc, char** argv) { ; ALOGD("Generating %s...", outputLayersTracePath); std::cout << "Generating " << outputLayersTracePath << "\n"; + + // sink any log spam from the stubbed surfaceflinger + __android_log_set_logger([](const struct __android_log_message* /* log_message */) {}); + if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) { std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath; return -1; diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index d2c2e29a2f..e5de7591ef 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -178,8 +178,8 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& return NO_ERROR; } -void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) { - mPresentFence = presentFence; +void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) { + mPresentFence = std::move(presentFence); } void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 81d79f0777..c09bcce067 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -19,20 +19,34 @@ #include <condition_variable> #include <deque> #include <mutex> +#include <optional> #include <queue> #include <thread> #include <unordered_map> #include <unordered_set> #include <android-base/thread_annotations.h> +#include <android/gui/ITransactionCompletedListener.h> + #include <binder/IBinder.h> -#include <compositionengine/FenceResult.h> -#include <ftl/future.h> -#include <gui/ITransactionCompletedListener.h> +#include <gui/ListenerStats.h> +#include <gui/ReleaseCallbackId.h> +#include <renderengine/RenderEngine.h> #include <ui/Fence.h> +#include <ui/FenceResult.h> namespace android { +using gui::CallbackId; +using gui::FrameEventHistoryStats; +using gui::IListenerHash; +using gui::ITransactionCompletedListener; +using gui::JankData; +using gui::ListenerCallbacks; +using gui::ListenerStats; +using gui::ReleaseCallbackId; +using gui::TransactionStats; + class CallbackHandle : public RefBase { public: CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids, @@ -48,7 +62,7 @@ public: std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences; std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1; nsecs_t latchTime = -1; - uint32_t transformHint = 0; + std::optional<uint32_t> transformHint = std::nullopt; uint32_t currentMaxAcquiredBufferCount = 0; std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE}; CompositorTiming compositorTiming; @@ -70,7 +84,7 @@ public: status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle); void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks); - void addPresentFence(const sp<Fence>& presentFence); + void addPresentFence(sp<Fence>); void sendCallbacks(bool onCommitOnly); void clearCompletedTransactions() { diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index 900d566942..380301fc73 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -20,19 +20,26 @@ #include <memory> #include <mutex> #include <vector> +#include "renderengine/ExternalTexture.h" #include <gui/LayerState.h> #include <system/window.h> namespace android { -class CountDownLatch; +// Extends the client side composer state by resolving buffer. +class ResolvedComposerState : public ComposerState { +public: + ResolvedComposerState() = default; + ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); } + std::shared_ptr<renderengine::ExternalTexture> externalTexture; +}; struct TransactionState { TransactionState() = default; TransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& composerStates, + std::vector<ResolvedComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, @@ -40,7 +47,7 @@ struct TransactionState { bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks, int originPid, int originUid, uint64_t transactionId) : frameTimelineInfo(frameTimelineInfo), - states(composerStates), + states(std::move(composerStates)), displays(displayStates), flags(transactionFlags), applyToken(applyToken), @@ -59,9 +66,20 @@ struct TransactionState { // Invokes `void(const layer_state_t&)` visitor for matching layers. template <typename Visitor> void traverseStatesWithBuffers(Visitor&& visitor) const { - for (const auto& [state] : states) { - if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) { - visitor(state); + for (const auto& state : states) { + if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && + state.state.surface) { + visitor(state.state); + } + } + } + + template <typename Visitor> + void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const { + for (const auto& state : states) { + if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && + state.state.surface) { + if (!visitor(state.state)) return; } } } @@ -72,8 +90,8 @@ struct TransactionState { bool isFrameActive() const { if (!displays.empty()) return true; - for (const auto& [state] : states) { - if (state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) { + for (const auto& state : states) { + if (state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) { return true; } } @@ -82,7 +100,7 @@ struct TransactionState { } FrameTimelineInfo frameTimelineInfo; - Vector<ComposerState> states; + std::vector<ResolvedComposerState> states; Vector<DisplayState> displays; uint32_t flags; sp<IBinder> applyToken; @@ -97,51 +115,7 @@ struct TransactionState { int originPid; int originUid; uint64_t id; - std::shared_ptr<CountDownLatch> transactionCommittedSignal; - int64_t queueTime = 0; bool sentFenceTimeoutWarning = false; }; -class CountDownLatch { -public: - enum { - eSyncTransaction = 1 << 0, - eSyncInputWindows = 1 << 1, - }; - explicit CountDownLatch(uint32_t flags) : mFlags(flags) {} - - // True if there is no waiting condition after count down. - bool countDown(uint32_t flag) { - std::unique_lock<std::mutex> lock(mMutex); - if (mFlags == 0) { - return true; - } - mFlags &= ~flag; - if (mFlags == 0) { - mCountDownComplete.notify_all(); - return true; - } - return false; - } - - // Return true if triggered. - bool wait_until(const std::chrono::nanoseconds& timeout) const { - std::unique_lock<std::mutex> lock(mMutex); - const auto untilTime = std::chrono::system_clock::now() + timeout; - while (mFlags != 0) { - // Conditional variables can be woken up sporadically, so we check count - // to verify the wakeup was triggered by |countDown|. - if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) { - return false; - } - } - return true; - } - -private: - uint32_t mFlags; - mutable std::condition_variable mCountDownComplete; - mutable std::mutex mMutex; -}; - } // namespace android diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp index 4497cafa58..bc9b870075 100644 --- a/services/surfaceflinger/TunnelModeEnabledReporter.cpp +++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp @@ -59,7 +59,7 @@ void TunnelModeEnabledReporter::binderDied(const wp<IBinder>& who) { void TunnelModeEnabledReporter::addListener(const sp<gui::ITunnelModeEnabledListener>& listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); - asBinder->linkToDeath(this); + asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); bool tunnelModeEnabled = false; { std::scoped_lock lock(mMutex); diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h new file mode 100644 index 0000000000..ee942177e5 --- /dev/null +++ b/services/surfaceflinger/Utils/Dumper.h @@ -0,0 +1,119 @@ +/* + * Copyright 2022 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 <string_view> + +#include <ftl/optional.h> + +namespace android::utils { + +// Dumps variables by appending their name and value to the output string. A variable is formatted +// as "name=value". If the name or value is empty, the format is "value" or "name=", respectively. +// A value of user-defined type T is stringified via `std::string to_string(const T&)`, which must +// be defined in the same namespace as T per the rules of ADL (argument-dependent lookup). +// +// TODO(b/249828573): Consolidate with <compositionengine/impl/DumpHelpers.h> +class Dumper { +public: + explicit Dumper(std::string& out) : mOut(out) {} + + void eol() { mOut += '\n'; } + + void dump(std::string_view name, std::string_view value = {}) { + using namespace std::string_view_literals; + + for (int i = mIndent; i-- > 0;) mOut += " "sv; + mOut += name; + if (!name.empty()) mOut += '='; + mOut += value; + eol(); + } + + void dump(std::string_view name, const std::string& value) { + dump(name, static_cast<const std::string_view&>(value)); + } + + void dump(std::string_view name, bool value) { + using namespace std::string_view_literals; + dump(name, value ? "true"sv : "false"sv); + } + + template <typename T> + void dump(std::string_view name, const std::optional<T>& opt) { + if (opt) { + dump(name, *opt); + } else { + using namespace std::string_view_literals; + dump(name, "nullopt"sv); + } + } + + template <typename T> + void dump(std::string_view name, const ftl::Optional<T>& opt) { + dump(name, static_cast<const std::optional<T>&>(opt)); + } + + template <typename T, typename... Ts> + void dump(std::string_view name, const T& value, const Ts&... rest) { + std::string string; + + constexpr bool kIsTuple = sizeof...(Ts) > 0; + if constexpr (kIsTuple) { + string += '{'; + } + + using std::to_string; + string += to_string(value); + + if constexpr (kIsTuple) { + string += ((", " + to_string(rest)) + ...); + string += '}'; + } + + dump(name, string); + } + + struct Indent { + explicit Indent(Dumper& dumper) : dumper(dumper) { dumper.mIndent++; } + ~Indent() { dumper.mIndent--; } + + Dumper& dumper; + }; + + struct Section { + Section(Dumper& dumper, std::string_view heading) : dumper(dumper) { + dumper.dump({}, heading); + indent.emplace(dumper); + } + + ~Section() { + indent.reset(); + dumper.eol(); + } + + Dumper& dumper; + std::optional<Indent> indent; + }; + +private: + std::string& mOut; + int mIndent = 0; +}; + +} // namespace android::utils diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp index 30b9d8f1cb..a1313e3a03 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -26,25 +26,43 @@ using gui::DisplayInfo; using gui::IWindowInfosListener; using gui::WindowInfo; -struct WindowInfosListenerInvoker::WindowInfosReportedListener - : gui::BnWindowInfosReportedListener { - explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker) : mInvoker(invoker) {} +struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener, + DeathRecipient { + explicit WindowInfosReportedListener( + size_t callbackCount, + const std::unordered_set<sp<gui::IWindowInfosReportedListener>, + SpHash<gui::IWindowInfosReportedListener>>& + windowInfosReportedListeners) + : mCallbacksPending(callbackCount), + mWindowInfosReportedListeners(windowInfosReportedListeners) {} binder::Status onWindowInfosReported() override { - mInvoker.windowInfosReported(); + // TODO(b/222421815) There could potentially be callbacks that we don't need to wait for + // before calling the WindowInfosReportedListeners coming from InputWindowCommands. Filter + // the list of callbacks down to those from system server. + if (--mCallbacksPending == 0) { + for (const auto& listener : mWindowInfosReportedListeners) { + sp<IBinder> asBinder = IInterface::asBinder(listener); + if (asBinder->isBinderAlive()) { + listener->onWindowInfosReported(); + } + } + } return binder::Status::ok(); } - WindowInfosListenerInvoker& mInvoker; -}; + void binderDied(const wp<IBinder>&) { onWindowInfosReported(); } -WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger) - : mFlinger(flinger), - mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {} +private: + std::atomic<size_t> mCallbacksPending; + std::unordered_set<sp<gui::IWindowInfosReportedListener>, + SpHash<gui::IWindowInfosReportedListener>> + mWindowInfosReportedListeners; +}; void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); - asBinder->linkToDeath(this); + asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); std::scoped_lock lock(mListenersMutex); mWindowInfosListeners.try_emplace(asBinder, std::move(listener)); @@ -55,7 +73,7 @@ void WindowInfosListenerInvoker::removeWindowInfosListener( sp<IBinder> asBinder = IInterface::asBinder(listener); std::scoped_lock lock(mListenersMutex); - asBinder->unlinkToDeath(this); + asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this)); mWindowInfosListeners.erase(asBinder); } @@ -64,9 +82,11 @@ void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) { mWindowInfosListeners.erase(who); } -void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos, - const std::vector<DisplayInfo>& displayInfos, - bool shouldSync) { +void WindowInfosListenerInvoker::windowInfosChanged( + const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos, + const std::unordered_set<sp<gui::IWindowInfosReportedListener>, + SpHash<gui::IWindowInfosReportedListener>>& + windowInfosReportedListeners) { ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners; { std::scoped_lock lock(mListenersMutex); @@ -75,18 +95,25 @@ void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo } } - mCallbacksPending = windowInfosListeners.size(); - + auto windowInfosReportedListener = windowInfosReportedListeners.empty() + ? nullptr + : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(), + windowInfosReportedListeners); for (const auto& listener : windowInfosListeners) { - listener->onWindowInfosChanged(windowInfos, displayInfos, - shouldSync ? mWindowInfosReportedListener : nullptr); - } -} + sp<IBinder> asBinder = IInterface::asBinder(listener); + + // linkToDeath is used here to ensure that the windowInfosReportedListeners + // are called even if one of the windowInfosListeners dies before + // calling onWindowInfosReported. + if (windowInfosReportedListener) { + asBinder->linkToDeath(windowInfosReportedListener); + } -void WindowInfosListenerInvoker::windowInfosReported() { - mCallbacksPending--; - if (mCallbacksPending == 0) { - mFlinger.windowInfosReported(); + auto status = listener->onWindowInfosChanged(windowInfos, displayInfos, + windowInfosReportedListener); + if (windowInfosReportedListener && !status.isOk()) { + windowInfosReportedListener->onWindowInfosReported(); + } } } diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h index d8d8d0f570..a1d66a186e 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.h +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -29,22 +29,21 @@ class SurfaceFlinger; class WindowInfosListenerInvoker : public IBinder::DeathRecipient { public: - explicit WindowInfosListenerInvoker(SurfaceFlinger&); - void addWindowInfosListener(sp<gui::IWindowInfosListener>); void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); void windowInfosChanged(const std::vector<gui::WindowInfo>&, - const std::vector<gui::DisplayInfo>&, bool shouldSync); + const std::vector<gui::DisplayInfo>&, + const std::unordered_set<sp<gui::IWindowInfosReportedListener>, + SpHash<gui::IWindowInfosReportedListener>>& + windowInfosReportedListeners); protected: void binderDied(const wp<IBinder>& who) override; private: struct WindowInfosReportedListener; - void windowInfosReported(); - SurfaceFlinger& mFlinger; std::mutex mListenersMutex; static constexpr size_t kStaticCapacity = 3; @@ -52,7 +51,6 @@ private: mWindowInfosListeners GUARDED_BY(mListenersMutex); sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener; - std::atomic<size_t> mCallbacksPending{0}; }; } // namespace android diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index b0b6bf14ab..7350e09cb5 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -127,3 +127,13 @@ cc_fuzz { "surfaceflinger_layer_fuzzer.cpp", ], } + +cc_fuzz { + name: "surfaceflinger_frametracer_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + ], + srcs: [ + "surfaceflinger_frametracer_fuzzer.cpp", + ], +} diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md index 78a7596ef3..a06c41b139 100644 --- a/services/surfaceflinger/fuzzer/README.md +++ b/services/surfaceflinger/fuzzer/README.md @@ -4,6 +4,7 @@ + [DisplayHardware](#DisplayHardware) + [Scheduler](#Scheduler) + [Layer](#Layer) ++ [FrameTracer](#FrameTracer) # <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger @@ -77,9 +78,8 @@ You can find the possible values in the fuzzer's source code. Layer supports the following parameters: 1. Display Connection Types (parameter name: `fakeDisplay`) 2. State Sets (parameter name: `traverseInZOrder`) -3. State Subsets (parameter name: `prepareCompositionState`) -4. Disconnect modes (parameter name: `disconnect`) -5. Data Spaces (parameter name: `setDataspace`) +3. Disconnect modes (parameter name: `disconnect`) +4. Data Spaces (parameter name: `setDataspace`) You can find the possible values in the fuzzer's source code. @@ -93,3 +93,16 @@ You can find the possible values in the fuzzer's source code. $ adb sync data $ adb shell /data/fuzz/arm64/surfaceflinger_layer_fuzzer/surfaceflinger_layer_fuzzer ``` + +# <a name="FrameTracer"></a> Fuzzer for FrameTracer + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) surfaceflinger_frametracer_fuzzer +``` +2. To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/surfaceflinger_frametracer_fuzzer/surfaceflinger_frametracer_fuzzer +``` diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp index a605a2fd9b..8a6af10f58 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -116,7 +116,8 @@ static constexpr hal::HWConfigId kActiveConfig = 0; class DisplayHardwareFuzzer { public: DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { - mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value(); + mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or( + PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>())); }; void process(); @@ -325,8 +326,8 @@ void DisplayHardwareFuzzer::invokeAidlComposer() { invokeComposerHal2_3(&composer, display, outLayer); invokeComposerHal2_4(&composer, display, outLayer); - composer.executeCommands(); - composer.resetCommands(); + composer.executeCommands(display); + composer.resetCommands(display); composer.destroyLayer(display, outLayer); composer.destroyVirtualDisplay(display); @@ -480,8 +481,8 @@ void DisplayHardwareFuzzer::invokeFrameBufferSurface() { BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); sp<FramebufferSurface> surface = - new FramebufferSurface(mHwc, mPhysicalDisplayId, bqConsumer, getFuzzedSize() /*size*/, - getFuzzedSize() /*maxSize*/); + sp<FramebufferSurface>::make(mHwc, mPhysicalDisplayId, bqConsumer, + getFuzzedSize() /*size*/, getFuzzedSize() /*maxSize*/); surface->beginFrame(mFdp.ConsumeBool()); surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); @@ -497,15 +498,15 @@ void DisplayHardwareFuzzer::invokeVirtualDisplaySurface() { DisplayIdGenerator<HalVirtualDisplayId> mGenerator; VirtualDisplayId VirtualDisplayId = mGenerator.generateId().value(); - sp<SurfaceComposerClient> mClient = new SurfaceComposerClient(); + sp<SurfaceComposerClient> mClient = sp<SurfaceComposerClient>::make(); sp<SurfaceControl> mSurfaceControl = mClient->createSurface(String8("TestSurface"), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, /*parent*/ nullptr); - sp<BLASTBufferQueue> mBlastBufferQueueAdapter = - new BLASTBufferQueue("TestBLASTBufferQueue", mSurfaceControl, 100, 100, - PIXEL_FORMAT_RGBA_8888); + auto mBlastBufferQueueAdapter = + sp<BLASTBufferQueue>::make("TestBLASTBufferQueue", mSurfaceControl, 100, 100, + PIXEL_FORMAT_RGBA_8888); sp<IGraphicBufferProducer> sink = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); sp<IGraphicBufferProducer> bqProducer = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); @@ -513,9 +514,9 @@ void DisplayHardwareFuzzer::invokeVirtualDisplaySurface() { BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); BufferQueue::createBufferQueue(&sink, &bqConsumer); - sp<VirtualDisplaySurface> surface = - new VirtualDisplaySurface(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer, - mFdp.ConsumeRandomLengthString().c_str() /*name*/); + auto surface = + sp<VirtualDisplaySurface>::make(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer, + mFdp.ConsumeRandomLengthString().c_str() /*name*/); surface->beginFrame(mFdp.ConsumeBool()); surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); @@ -565,7 +566,7 @@ void DisplayHardwareFuzzer::invokeComposer() { mHwc.getLayerReleaseFence(halDisplayID, layer); - mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make().get(), sp<GraphicBuffer>::make()); + mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make(), sp<GraphicBuffer>::make()); mHwc.clearReleaseFences(halDisplayID); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp new file mode 100644 index 0000000000..a22a778989 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2021 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 <FrameTracer/FrameTracer.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <perfetto/trace/trace.pb.h> + +namespace android::fuzz { + +using namespace google::protobuf; + +constexpr size_t kMaxStringSize = 100; +constexpr size_t kMinLayerIds = 1; +constexpr size_t kMaxLayerIds = 10; +constexpr int32_t kConfigDuration = 500; +constexpr int32_t kBufferSize = 1024; +constexpr int32_t kTimeOffset = 100000; + +class FrameTracerFuzzer { +public: + FrameTracerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { + // Fuzzer is single-threaded, so no need to be thread-safe. + static bool wasInitialized = false; + if (!wasInitialized) { + perfetto::TracingInitArgs args; + args.backends = perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + wasInitialized = true; + } + mFrameTracer = std::make_unique<android::FrameTracer>(); + } + ~FrameTracerFuzzer() { mFrameTracer.reset(); } + void process(); + +private: + std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest(); + void traceTimestamp(); + std::vector<int32_t> generateLayerIds(size_t numLayerIds); + void traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds); + void traceFence(std::vector<int32_t> layerIds, size_t numLayerIds); + std::unique_ptr<android::FrameTracer> mFrameTracer = nullptr; + FuzzedDataProvider mFdp; + android::FenceToFenceTimeMap mFenceFactory; +}; + +std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionForTest() { + perfetto::TraceConfig cfg; + cfg.set_duration_ms(kConfigDuration); + cfg.add_buffers()->set_size_kb(kBufferSize); + auto* dsCfg = cfg.add_data_sources()->mutable_config(); + dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource); + + auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend); + tracingSession->Setup(cfg); + return tracingSession; +} + +std::vector<int32_t> FrameTracerFuzzer::generateLayerIds(size_t numLayerIds) { + std::vector<int32_t> layerIds; + for (size_t i = 0; i < numLayerIds; ++i) { + layerIds.push_back(mFdp.ConsumeIntegral<int32_t>()); + } + return layerIds; +} + +void FrameTracerFuzzer::traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds) { + int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1)); + mFrameTracer->traceTimestamp(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/, + mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, + mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/, + android::FrameTracer::FrameEvent::UNSPECIFIED, + mFdp.ConsumeIntegral<nsecs_t>() /*duration*/); +} + +void FrameTracerFuzzer::traceFence(std::vector<int32_t> layerIds, size_t numLayerIds) { + const nsecs_t signalTime = systemTime(); + const nsecs_t startTime = signalTime + kTimeOffset; + auto fence = mFenceFactory.createFenceTimeForTest(android::Fence::NO_FENCE); + mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, signalTime); + int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1)); + mFrameTracer->traceFence(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/, + mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, fence, + android::FrameTracer::FrameEvent::ACQUIRE_FENCE, startTime); +} + +void FrameTracerFuzzer::process() { + mFrameTracer->registerDataSource(); + + auto tracingSession = getTracingSessionForTest(); + tracingSession->StartBlocking(); + + size_t numLayerIds = mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds); + std::vector<int32_t> layerIds = generateLayerIds(numLayerIds); + + for (auto it = layerIds.begin(); it != layerIds.end(); ++it) { + mFrameTracer->traceNewLayer(*it /*layerId*/, + mFdp.ConsumeRandomLengthString(kMaxStringSize) /*layerName*/); + } + + traceTimestamp(layerIds, numLayerIds); + traceFence(layerIds, numLayerIds); + + mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime()); + + tracingSession->StopBlocking(); + + for (auto it = layerIds.begin(); it != layerIds.end(); ++it) { + mFrameTracer->onDestroy(*it); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FrameTracerFuzzer frameTracerFuzzer(data, size); + frameTracerFuzzer.process(); + return 0; +} + +} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp index f25043cd6d..ce4d18fbe1 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp @@ -103,7 +103,7 @@ static constexpr uint32_t kMaxCode = 1050; class SurfaceFlingerFuzzer { public: SurfaceFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) { - mFlinger = mTestableFlinger.flinger(); + mFlinger = sp<SurfaceFlinger>::fromExisting(mTestableFlinger.flinger()); }; void process(const uint8_t *data, size_t size); @@ -130,7 +130,7 @@ void SurfaceFlingerFuzzer::invokeFlinger() { mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>(); mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>(); mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>(); - mFlinger->hasWideColorDisplay = mFdp.ConsumeBool(); + mTestableFlinger.mutableSupportsWideColor() = mFdp.ConsumeBool(); mFlinger->useContextPriority = mFdp.ConsumeBool(); mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces); @@ -150,8 +150,7 @@ void SurfaceFlingerFuzzer::invokeFlinger() { sp<IBinder> handle = defaultServiceManager()->checkService( String16(mFdp.ConsumeRandomLengthString().c_str())); - mFlinger->fromHandle(handle); - mFlinger->windowInfosReported(); + LayerHandle::getLayer(handle); mFlinger->disableExpensiveRendering(); } @@ -238,7 +237,8 @@ void SurfaceFlingerFuzzer::process(const uint8_t *data, size_t size) { mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool()); - mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>()); + FTL_FAKE_GUARD(kMainThreadContext, + mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>())); mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>()); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 867a1985bd..81ca659915 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -30,19 +30,16 @@ #include <ui/DisplayStatInfo.h> #include <ui/DynamicDisplayInfo.h> -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" -#include "ContainerLayer.h" #include "DisplayDevice.h" #include "DisplayHardware/ComposerHal.h" -#include "EffectLayer.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" +#include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/EventThread.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncController.h" @@ -50,7 +47,6 @@ #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerDefaultFactory.h" -#include "SurfaceInterceptor.h" #include "ThreadContext.h" #include "TimeStats/TimeStats.h" @@ -64,7 +60,6 @@ #include "tests/unittests/mock/MockFrameTimeline.h" #include "tests/unittests/mock/MockFrameTracer.h" #include "tests/unittests/mock/MockNativeWindowSurface.h" -#include "tests/unittests/mock/MockSurfaceInterceptor.h" #include "tests/unittests/mock/MockTimeStats.h" #include "tests/unittests/mock/MockVSyncTracker.h" #include "tests/unittests/mock/MockVsyncController.h" @@ -86,7 +81,6 @@ using types::V1_0::Transform; using types::V1_1::RenderIntent; using types::V1_2::ColorMode; using types::V1_2::Dataspace; -using types::V1_2::Hdr; using types::V1_2::PixelFormat; using V2_1::Config; @@ -155,14 +149,22 @@ static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888, ui::PixelFormat::YCBCR_P010, ui::PixelFormat::HSV_888}; -FloatRect getFuzzedFloatRect(FuzzedDataProvider *fdp) { +inline VsyncId getFuzzedVsyncId(FuzzedDataProvider& fdp) { + return VsyncId{fdp.ConsumeIntegral<int64_t>()}; +} + +inline TimePoint getFuzzedTimePoint(FuzzedDataProvider& fdp) { + return TimePoint::fromNs(fdp.ConsumeIntegral<nsecs_t>()); +} + +inline FloatRect getFuzzedFloatRect(FuzzedDataProvider* fdp) { return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/, fdp->ConsumeFloatingPoint<float>() /*right*/, fdp->ConsumeFloatingPoint<float>() /*top*/, fdp->ConsumeFloatingPoint<float>() /*bottom*/); } -HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider *fdp) { +inline HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider* fdp) { HdrMetadata hdrMetadata; if (fdp->ConsumeBool()) { hdrMetadata.cta8613.maxContentLightLevel = fdp->ConsumeFloatingPoint<float>(); @@ -215,18 +217,21 @@ namespace scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs, - ISchedulerCallback &callback) + TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, + ISchedulerCallback& callback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs, + std::make_unique<android::mock::VSyncTracker>(), selectorPtr, callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, std::unique_ptr<VSyncTracker> tracker, - std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback) + std::shared_ptr<RefreshRateSelector> selectorPtr, + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateConfigs(std::move(configs)); + + const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); + registerDisplay(displayId, std::move(selectorPtr)); } ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -238,7 +243,7 @@ public: auto &mutableLayerHistory() { return mLayerHistory; } - auto refreshRateConfigs() { return holdRefreshRateConfigs(); } + auto refreshRateSelector() { return leaderSelectorPtr(); } void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { @@ -266,14 +271,15 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) { return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } private: // ICompositor overrides: - bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } - void composite(nsecs_t, int64_t) override {} + void configure() override {} + bool commit(TimePoint, VsyncId, TimePoint) override { return false; } + void composite(TimePoint, VsyncId) override {} void sample() override {} // MessageQueue overrides: @@ -286,13 +292,18 @@ private: namespace surfaceflinger::test { class Factory final : public surfaceflinger::Factory { + struct NoOpMessageQueue : android::impl::MessageQueue { + using android::impl::MessageQueue::MessageQueue; + void onFrameSignal(ICompositor&, VsyncId, TimePoint) override {} + }; + public: ~Factory() = default; - std::unique_ptr<HWComposer> createHWComposer(const std::string &) override { return nullptr; } + std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; } - std::unique_ptr<MessageQueue> createMessageQueue(ICompositor &compositor) { - return std::make_unique<android::impl::MessageQueue>(compositor); + std::unique_ptr<MessageQueue> createMessageQueue(ICompositor& compositor) { + return std::make_unique<NoOpMessageQueue>(compositor); } std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( @@ -301,27 +312,23 @@ public: } std::unique_ptr<scheduler::Scheduler> createScheduler( - const std::shared_ptr<scheduler::RefreshRateConfigs> &, - scheduler::ISchedulerCallback &) { + const std::shared_ptr<scheduler::RefreshRateSelector>&, + scheduler::ISchedulerCallback&) { return nullptr; } - sp<SurfaceInterceptor> createSurfaceInterceptor() override { - return new android::impl::SurfaceInterceptor(); - } - sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { - return new StartPropertySetThread(timestampPropertyValue); + return sp<StartPropertySetThread>::make(timestampPropertyValue); } sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs &creationArgs) override { - return new DisplayDevice(creationArgs); + return sp<DisplayDevice>::make(creationArgs); } sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, std::string requestorName) override { - return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName); } void createBufferQueue(sp<IGraphicBufferProducer> *outProducer, @@ -334,18 +341,6 @@ public: mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); } - sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer, - const sp<SurfaceFlinger> &flinger, - const wp<Layer> &layer) override { - return new MonitoredProducer(producer, flinger, layer); - } - - sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer, - renderengine::RenderEngine &renderEngine, - uint32_t textureName, Layer *layer) override { - return new BufferLayerConsumer(consumer, renderEngine, textureName, layer); - } - std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( const sp<IGraphicBufferProducer> &producer) override { if (!mCreateNativeWindowSurface) return nullptr; @@ -356,20 +351,14 @@ public: return compositionengine::impl::createCompositionEngine(); } - sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs &) override { - return nullptr; - } + sp<Layer> createBufferStateLayer(const LayerCreationArgs &) override { return nullptr; } - sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override { - return nullptr; + sp<Layer> createEffectLayer(const LayerCreationArgs &args) override { + return sp<Layer>::make(args); } - sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override { - return new EffectLayer(args); - } - - sp<ContainerLayer> createContainerLayer(const LayerCreationArgs &args) override { - return new ContainerLayer(args); + sp<LayerFE> createLayerFE(const std::string &layerName) override { + return sp<LayerFE>::make(layerName); } std::unique_ptr<FrameTracer> createFrameTracer() override { @@ -430,7 +419,7 @@ public: void onPullAtom(FuzzedDataProvider *fdp) { const int32_t atomId = fdp->ConsumeIntegral<uint8_t>(); - std::string pulledData = fdp->ConsumeRandomLengthString().c_str(); + std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>(); bool success = fdp->ConsumeBool(); mFlinger->onPullAtom(atomId, &pulledData, &success); } @@ -447,15 +436,13 @@ public: mFlinger->clearStatsLocked(dumpArgs, result); mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result); - FTL_FAKE_GUARD(kMainThreadContext, mFlinger->logFrameStats()); + FTL_FAKE_GUARD(kMainThreadContext, + mFlinger->logFrameStats(TimePoint::fromNs(fdp->ConsumeIntegral<nsecs_t>()))); result = fdp->ConsumeRandomLengthString().c_str(); mFlinger->dumpFrameTimeline(dumpArgs, result); result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->dumpStaticScreenStats(result); - - result = fdp->ConsumeRandomLengthString().c_str(); mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result); LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>()); @@ -469,10 +456,6 @@ public: mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>()); mFlinger->updateColorMatrixLocked(); mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>()); - - const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>()); - mFlinger->waitForSynchronousTransaction(transactionCommittedSignal); - mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>()); } void getCompositionPreference() { @@ -508,14 +491,14 @@ public: mFlinger->getDisplayState(display, &displayState); } - void getStaticDisplayInfo(sp<IBinder> &display) { + void getStaticDisplayInfo(int64_t displayId) { ui::StaticDisplayInfo staticDisplayInfo; - mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo); + mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo); } - void getDynamicDisplayInfo(sp<IBinder> &display) { + void getDynamicDisplayInfo(int64_t displayId) { android::ui::DynamicDisplayInfo dynamicDisplayInfo; - mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo); + mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo); } void getDisplayNativePrimaries(sp<IBinder> &display) { android::ui::DisplayPrimaries displayPrimaries; @@ -523,16 +506,8 @@ public: } void getDesiredDisplayModeSpecs(sp<IBinder> &display) { - ui::DisplayModeId outDefaultMode; - bool outAllowGroupSwitching; - float outPrimaryRefreshRateMin; - float outPrimaryRefreshRateMax; - float outAppRequestRefreshRateMin; - float outAppRequestRefreshRateMax; - mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching, - &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax, - &outAppRequestRefreshRateMin, - &outAppRequestRefreshRateMax); + gui::DisplayModeSpecs _; + mFlinger->getDesiredDisplayModeSpecs(display, &_); } void setVsyncConfig(FuzzedDataProvider *fdp) { @@ -540,19 +515,16 @@ public: mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>()); } - void updateCompositorTiming(FuzzedDataProvider *fdp) { - std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE; - mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime); + // TODO(b/248317436): extend to cover all displays for multi-display devices + static std::optional<PhysicalDisplayId> getFirstDisplayId() { + std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds(); + if (ids.empty()) return {}; + return ids.front(); } - void getCompositorTiming() { - CompositorTiming compositorTiming; - mFlinger->getCompositorTiming(&compositorTiming); - } - - sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) { + std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) { mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool()); - mFlinger->createConnection(); + const sp<Client> client = sp<Client>::make(mFlinger); DisplayIdGenerator<HalVirtualDisplayId> kGenerator; HalVirtualDisplayId halVirtualDisplayId = kGenerator.generateId().value(); @@ -561,7 +533,8 @@ public: ui::PixelFormat pixelFormat{}; mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat); - PhysicalDisplayId physicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value(); + PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or( + PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>())); mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId); sp<IBinder> display = @@ -576,36 +549,32 @@ public: mFlinger->bootFinished(); - return display; + return {display, physicalDisplayId.value}; } void fuzzSurfaceFlinger(const uint8_t *data, size_t size) { FuzzedDataProvider mFdp(data, size); - sp<IBinder> display = fuzzBoot(&mFdp); + auto [display, displayId] = fuzzBoot(&mFdp); sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make(); - mFlinger->authenticateSurfaceTexture(bufferProducer.get()); mFlinger->createDisplayEventConnection(); getDisplayStats(display); getDisplayState(display); - getStaticDisplayInfo(display); - getDynamicDisplayInfo(display); + getStaticDisplayInfo(displayId); + getDynamicDisplayInfo(displayId); getDisplayNativePrimaries(display); mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); mFlinger->setGameContentType(display, mFdp.ConsumeBool()); mFlinger->setPowerMode(display, mFdp.ConsumeIntegral<int>()); - mFlinger->clearAnimationFrameStats(); overrideHdrTypes(display, &mFdp); onPullAtom(&mFdp); - mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>()); - getCompositionPreference(); getDisplayedContentSample(display, &mFdp); getDesiredDisplayModeSpecs(display); @@ -620,19 +589,24 @@ public: mFlinger->binderDied(display); mFlinger->onFirstRef(); - mFlinger->commitTransactions(); mFlinger->updateInputFlinger(); mFlinger->updateCursorAsync(); setVsyncConfig(&mFdp); - mFlinger->flushTransactionQueues(0); + { + ftl::FakeGuard guard(kMainThreadContext); + + mFlinger->commitTransactions(); + mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); + mFlinger->postComposition(); + } mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); mFlinger->commitOffscreenLayers(); - mFlinger->frameIsEarly(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeIntegral<int64_t>()); + mFlinger->frameIsEarly(getFuzzedTimePoint(mFdp), getFuzzedVsyncId(mFdp)); mFlinger->computeLayerBounds(); mFlinger->startBootAnim(); @@ -643,14 +617,6 @@ public: mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>()); - mFlinger->postComposition(); - - getCompositorTiming(); - - updateCompositorTiming(&mFdp); - - mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>()); - FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postFrame()); mFlinger->calculateExpectedPresentTime({}); mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool()); @@ -661,7 +627,8 @@ public: } void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine)); + mFlinger->mRenderEngine = std::move(renderEngine); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { @@ -688,8 +655,8 @@ public: modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); } - mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); - const auto fps = mRefreshRateConfigs->getActiveMode()->getFps(); + mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); + const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps(); mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -698,7 +665,7 @@ public: hal::PowerMode::OFF); mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), - std::move(vsyncTracker), mRefreshRateConfigs, + std::move(vsyncTracker), mRefreshRateSelector, *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); @@ -740,9 +707,9 @@ public: void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); } - auto commitTransactionsLocked(uint32_t transactionFlags) { + void commitTransactionsLocked(uint32_t transactionFlags) FTL_FAKE_GUARD(kMainThreadContext) { Mutex::Autolock lock(mFlinger->mStateLock); - return mFlinger->commitTransactionsLocked(transactionFlags); + mFlinger->commitTransactionsLocked(transactionFlags); } auto setDisplayStateLocked(const DisplayState &s) { @@ -758,28 +725,35 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto &getTransactionQueue() { return mFlinger->mTransactionQueue; } - auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; } + auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; } + auto &getPendingTransactionQueue() { + return mFlinger->mTransactionHandler.mPendingTransactionQueues; + } - auto setTransactionState( - const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states, - const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken, - const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks, - std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) { + auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo, + Vector<ComposerState> &states, const Vector<DisplayState> &displays, + uint32_t flags, const sp<IBinder> &applyToken, + const InputWindowCommands &inputWindowCommands, + int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t &uncacheBuffer, bool hasListenerCallbacks, + std::vector<ListenerCallbacks> &listenerCallbacks, + uint64_t transactionId) { return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, uncacheBuffer, hasListenerCallbacks, listenerCallbacks, transactionId); } - auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); }; + auto flushTransactionQueues() { + ftl::FakeGuard guard(kMainThreadContext); + return mFlinger->flushTransactionQueues(VsyncId{0}); + } auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { return mFlinger->onTransact(code, data, reply, flags); } - auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); } + auto getGpuContextPriority() { return mFlinger->getGpuContextPriority(); } auto calculateMaxAcquiredBufferCount(Fps refreshRate, std::chrono::nanoseconds presentLatency) const { @@ -789,35 +763,34 @@ public: /* Read-write access to private data to set up preconditions and assert * post-conditions. */ + auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; } + auto& mutableCurrentState() { return mFlinger->mCurrentState; } + auto& mutableDisplays() { return mFlinger->mDisplays; } + auto& mutableDrawingState() { return mFlinger->mDrawingState; } - auto &mutableCurrentState() { return mFlinger->mCurrentState; } - auto &mutableDisplays() { return mFlinger->mDisplays; } - auto &mutableDrawingState() { return mFlinger->mDrawingState; } - auto &mutableInterceptor() { return mFlinger->mInterceptor; } - - auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); } + auto fromHandle(const sp<IBinder> &handle) { return LayerHandle::getLayer(handle); } ~TestableSurfaceFlinger() { mutableDisplays().clear(); mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); - mutableInterceptor().clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mFlinger->mCompositionEngine->setRenderEngine( - std::unique_ptr<renderengine::RenderEngine>()); + mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } private: void setVsyncEnabled(bool) override {} - void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {} + void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} surfaceflinger::test::Factory mFactory; - sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); + sp<SurfaceFlinger> mFlinger = + sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); scheduler::TestableScheduler *mScheduler = nullptr; - std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; }; } // namespace android diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp index 34cf90628a..acfc1d4dc8 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp @@ -14,13 +14,9 @@ * limitations under the License. * */ -#include <BufferStateLayer.h> #include <Client.h> #include <DisplayDevice.h> -#include <EffectLayer.h> -#include <LayerRejecter.h> #include <LayerRenderArea.h> -#include <MonitoredProducer.h> #include <ftl/future.h> #include <fuzzer/FuzzedDataProvider.h> #include <gui/IProducerListener.h> @@ -59,8 +55,10 @@ Rect LayerFuzzer::getFuzzedRect() { } FrameTimelineInfo LayerFuzzer::getFuzzedFrameTimelineInfo() { - return FrameTimelineInfo{.vsyncId = mFdp.ConsumeIntegral<int64_t>(), - .inputEventId = mFdp.ConsumeIntegral<int32_t>()}; + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = mFdp.ConsumeIntegral<int64_t>(); + ftInfo.inputEventId = mFdp.ConsumeIntegral<int32_t>(); + return ftInfo; } LayerCreationArgs LayerFuzzer::createLayerCreationArgs(TestableSurfaceFlinger* flinger, @@ -77,15 +75,15 @@ LayerCreationArgs LayerFuzzer::createLayerCreationArgs(TestableSurfaceFlinger* f void LayerFuzzer::invokeEffectLayer() { TestableSurfaceFlinger flinger; - sp<Client> client = sp<Client>::make(flinger.flinger()); + sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger())); const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client); - sp<EffectLayer> effectLayer = sp<EffectLayer>::make(layerCreationArgs); + sp<Layer> effectLayer = sp<Layer>::make(layerCreationArgs); effectLayer->setColor({(mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*x*/, mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*y*/, mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*z*/)}); effectLayer->setDataspace(mFdp.PickValueInArray(kDataspaces)); - sp<EffectLayer> parent = sp<EffectLayer>::make(layerCreationArgs); + sp<Layer> parent = sp<Layer>::make(layerCreationArgs); effectLayer->setChildrenDrawingParent(parent); const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo(); @@ -109,24 +107,22 @@ void LayerFuzzer::invokeEffectLayer() { void LayerFuzzer::invokeBufferStateLayer() { TestableSurfaceFlinger flinger; - sp<Client> client = sp<Client>::make(flinger.flinger()); - sp<BufferStateLayer> layer = - sp<BufferStateLayer>::make(createLayerCreationArgs(&flinger, client)); + sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger())); + sp<Layer> layer = sp<Layer>::make(createLayerCreationArgs(&flinger, client)); sp<Fence> fence = sp<Fence>::make(); const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence); - const CompositorTiming compositor = {mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>()}; + const CompositorTiming compositorTiming(mFdp.ConsumeIntegral<int64_t>(), + mFdp.ConsumeIntegral<int64_t>(), + mFdp.ConsumeIntegral<int64_t>(), + mFdp.ConsumeIntegral<int64_t>()); layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share()); layer->onLayerDisplayed( ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share()); layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>()); - layer->finalizeFrameEventHistory(fenceTime, compositor); - layer->onPostComposition(nullptr, fenceTime, fenceTime, compositor); - layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>()); + layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming); layer->setTransform(mFdp.ConsumeIntegral<uint32_t>()); layer->setTransformToDisplayInverse(mFdp.ConsumeBool()); @@ -150,7 +146,6 @@ void LayerFuzzer::invokeBufferStateLayer() { layer->computeSourceBounds(getFuzzedFloatRect(&mFdp)); layer->fenceHasSignaled(); - layer->framePresentTimeIsCurrent(mFdp.ConsumeIntegral<int64_t>()); layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>()); const std::vector<sp<CallbackHandle>> callbacks; layer->setTransactionCompletedListeners(callbacks); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index da60a6981b..7959e52fba 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -19,8 +19,11 @@ #include <fuzzer/FuzzedDataProvider.h> #include <processgroup/sched_policy.h> +#include <scheduler/PresentLatencyTracker.h> + #include "Scheduler/DispSyncSource.h" #include "Scheduler/OneShotTimer.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" @@ -36,7 +39,7 @@ constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriod (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(), (120_Hz).getPeriodNsecs()}; -constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>(); +constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>(); constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; @@ -57,7 +60,8 @@ public: private: void fuzzRefreshRateSelection(); - void fuzzRefreshRateConfigs(); + void fuzzRefreshRateSelector(); + void fuzzPresentLatencyTracker(); void fuzzVSyncModulator(); void fuzzVSyncPredictor(); void fuzzVSyncReactor(); @@ -89,12 +93,12 @@ void SchedulerFuzzer::fuzzEventThread() { const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); }; std::unique_ptr<android::impl::EventThread> thread = std::make_unique< android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr, - nullptr, nullptr, getVsyncPeriod); + nullptr, getVsyncPeriod); thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool()); sp<EventThreadConnection> connection = - new EventThreadConnection(thread.get(), mFdp.ConsumeIntegral<uint16_t>(), nullptr, - {} /*eventRegistration*/); + sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>(), + nullptr); thread->requestNextVsync(connection); thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection); @@ -224,8 +228,8 @@ void SchedulerFuzzer::fuzzLayerHistory() { nsecs_t time2 = time1; uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>(); - sp<FuzzImplLayer> layer1 = new FuzzImplLayer(flinger.flinger()); - sp<FuzzImplLayer> layer2 = new FuzzImplLayer(flinger.flinger()); + sp<FuzzImplLayer> layer1 = sp<FuzzImplLayer>::make(flinger.flinger()); + sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger()); for (int i = 0; i < historySize; ++i) { historyV1.record(layer1.get(), time1, time1, @@ -235,8 +239,8 @@ void SchedulerFuzzer::fuzzLayerHistory() { time1 += mFdp.PickValueInArray(kVsyncPeriods); time2 += mFdp.PickValueInArray(kVsyncPeriods); } - historyV1.summarize(*scheduler->refreshRateConfigs(), time1); - historyV1.summarize(*scheduler->refreshRateConfigs(), time2); + historyV1.summarize(*scheduler->refreshRateSelector(), time1); + historyV1.summarize(*scheduler->refreshRateSelector(), time2); scheduler->createConnection(std::make_unique<android::mock::EventThread>()); @@ -245,7 +249,9 @@ void SchedulerFuzzer::fuzzLayerHistory() { scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); - dump<scheduler::TestableScheduler>(scheduler, &mFdp); + std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength); + utils::Dumper dumper(result); + scheduler->dump(dumper); } void SchedulerFuzzer::fuzzVSyncReactor() { @@ -260,7 +266,7 @@ void SchedulerFuzzer::fuzzVSyncReactor() { reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt, &periodFlushed); - sp<Fence> fence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); + sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING)); std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence); vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>()); FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>()); @@ -319,14 +325,14 @@ void SchedulerFuzzer::fuzzRefreshRateSelection() { LayerCreationArgs args(flinger.flinger(), client, mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata()); - sp<Layer> layer = new BufferQueueLayer(args); + sp<Layer> layer = sp<Layer>::make(args); layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>()); } -void SchedulerFuzzer::fuzzRefreshRateConfigs() { - using RefreshRateConfigs = scheduler::RefreshRateConfigs; - using LayerRequirement = RefreshRateConfigs::LayerRequirement; +void SchedulerFuzzer::fuzzRefreshRateSelector() { + using RefreshRateSelector = scheduler::RefreshRateSelector; + using LayerRequirement = RefreshRateSelector::LayerRequirement; using RefreshRateStats = scheduler::RefreshRateStats; const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1); @@ -342,49 +348,71 @@ void SchedulerFuzzer::fuzzRefreshRateConfigs() { Fps::fromValue(static_cast<float>(fps)))); } - RefreshRateConfigs refreshRateConfigs(displayModes, modeId); + RefreshRateSelector refreshRateSelector(displayModes, modeId); - const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; + const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}}; - refreshRateConfigs.getBestRefreshRate(layers, globalSignals); + refreshRateSelector.getRankedFrameRates(layers, globalSignals); layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>(); layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()); layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values); auto frameRateOverrides = - refreshRateConfigs.getFrameRateOverrides(layers, - Fps::fromValue( + refreshRateSelector.getFrameRateOverrides(layers, + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + globalSignals); + + { + ftl::FakeGuard guard(kMainThreadContext); + + refreshRateSelector.setPolicy( + RefreshRateSelector:: + DisplayManagerPolicy{modeId, + {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}}); + refreshRateSelector.setPolicy( + RefreshRateSelector::OverridePolicy{modeId, + {Fps::fromValue( mFdp.ConsumeFloatingPoint<float>()), - globalSignals); + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>())}}); + refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{}); - refreshRateConfigs.setDisplayManagerPolicy( - {modeId, - {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}}); - refreshRateConfigs.setActiveModeId(modeId); + refreshRateSelector.setActiveMode(modeId, + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); + } - RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>())); - RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); + RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>())); + RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); android::mock::TimeStats timeStats; RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), PowerMode::OFF); - const auto fpsOpt = displayModes.get(modeId, [](const auto& mode) { return mode->getFps(); }); + const auto fpsOpt = displayModes.get(modeId).transform( + [](const DisplayModePtr& mode) { return mode->getFps(); }); refreshRateStats.setRefreshRate(*fpsOpt); refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes)); } +void SchedulerFuzzer::fuzzPresentLatencyTracker() { + scheduler::PresentLatencyTracker tracker; + tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()), + FenceTime::NO_FENCE); +} + void SchedulerFuzzer::process() { fuzzRefreshRateSelection(); - fuzzRefreshRateConfigs(); + fuzzRefreshRateSelector(); + fuzzPresentLatencyTracker(); fuzzVSyncModulator(); fuzzVSyncPredictor(); fuzzVSyncReactor(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 1a49ead275..2bc5b4676f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -27,7 +27,6 @@ #include "Clock.h" #include "Layer.h" #include "Scheduler/EventThread.h" -#include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncModulator.h" @@ -117,6 +116,8 @@ public: return true; } + void setDivisor(unsigned) override {} + nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { return timePoint; diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index 973a4392a2..7287dd0103 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -44,10 +44,11 @@ cc_library { } java_library_static { - name: "layersprotosnano", + name: "layersprotoslite", host_supported: true, proto: { - type: "nano", + type: "lite", + include_dirs: ["external/protobuf/src"], }, srcs: ["*.proto"], sdk_version: "core_platform", @@ -56,7 +57,7 @@ java_library_static { jarjar_rules: "jarjar-rules.txt", }, host: { - static_libs: ["libprotobuf-java-nano"], + static_libs: ["libprotobuf-java-lite"], }, }, } diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index 52503bad3a..cdc2706ee2 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -24,6 +24,8 @@ #include <unordered_map> #include <vector> +using android::gui::LayerMetadata; + namespace android { namespace surfaceflinger { diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto index 13647b669e..804a4994ee 100644 --- a/services/surfaceflinger/layerproto/layerstrace.proto +++ b/services/surfaceflinger/layerproto/layerstrace.proto @@ -38,9 +38,13 @@ message LayersTraceFileProto { optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ repeated LayersTraceProto entry = 2; + + /* offset between real-time clock and elapsed time clock in nanoseconds. + Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */ + optional fixed64 real_to_elapsed_time_offset_nanos = 3; } -/* one window manager trace entry. */ +/* one layers trace entry. */ message LayersTraceProto { /* required: elapsed realtime in nanos since boot of when this entry was logged */ optional sfixed64 elapsed_realtime_nanos = 1; @@ -60,4 +64,6 @@ message LayersTraceProto { optional uint32 missed_entries = 6; repeated DisplayProto displays = 7; + + optional int64 vsync_id = 8; } diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto index 4f99b192a3..4c6a9cf85b 100644 --- a/services/surfaceflinger/layerproto/transactions.proto +++ b/services/surfaceflinger/layerproto/transactions.proto @@ -36,6 +36,10 @@ message TransactionTraceFile { fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ repeated TransactionTraceEntry entry = 2; + + /* offset between real-time clock and elapsed time clock in nanoseconds. + Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */ + fixed64 real_to_elapsed_time_offset_nanos = 3; } message TransactionTraceEntry { @@ -78,7 +82,7 @@ message LayerState { eChangesLsbNone = 0; ePositionChanged = 0x00000001; eLayerChanged = 0x00000002; - eSizeChanged = 0x00000004; + // unused = 0x00000004; eAlphaChanged = 0x00000008; eMatrixChanged = 0x00000010; @@ -94,8 +98,7 @@ message LayerState { eReparent = 0x00008000; eColorChanged = 0x00010000; - eDestroySurface = 0x00020000; - eTransformChanged = 0x00040000; + eBufferTransformChanged = 0x00040000; eTransformToDisplayInverseChanged = 0x00080000; eCropChanged = 0x00100000; diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index ec180548e1..fedd71e921 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -67,7 +67,7 @@ static void startDisplayService() { using android::frameworks::displayservice::V1_0::implementation::DisplayService; using android::frameworks::displayservice::V1_0::IDisplayService; - sp<IDisplayService> displayservice = new DisplayService(); + sp<IDisplayService> displayservice = sp<DisplayService>::make(); status_t err = displayservice->registerAsService(); // b/141930622 @@ -153,7 +153,7 @@ int main(int, char**) { IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO); // publish gui::ISurfaceComposer, the new AIDL interface - sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger); + sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger); sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO); diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index bcbe21a483..8540c3dcfc 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -445,6 +445,28 @@ prop { prop_name: "ro.surface_flinger.enable_frame_rate_override" } +# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate +# to native display refresh rates only. Before introducing this flag, native display refresh rates +# was the default behaviour. With this flag we can control which behaviour we want explicitly. +# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false. +prop { + api_name: "frame_rate_override_for_native_rates" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates" +} + +# Enables the frame rate override feature (enable_frame_rate_override) to +# override the frame rate globally instead of only for individual apps. +prop { + api_name: "frame_rate_override_global" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.frame_rate_override_global" +} + # Enables Layer Caching prop { api_name: "enable_layer_caching" diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 348a462038..93381333a8 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -61,6 +61,14 @@ props { prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" } prop { + api_name: "frame_rate_override_for_native_rates" + prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates" + } + prop { + api_name: "frame_rate_override_global" + prop_name: "ro.surface_flinger.frame_rate_override_global" + } + prop { api_name: "has_HDR_display" prop_name: "ro.surface_flinger.has_HDR_display" } diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 507601b4e0..bd6367d672 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -23,7 +23,10 @@ package { cc_test { name: "SurfaceFlinger_test", - defaults: ["surfaceflinger_defaults"], + defaults: [ + "android.hardware.graphics.common-ndk_shared", + "surfaceflinger_defaults", + ], test_suites: ["device-tests"], srcs: [ "BootDisplayMode_test.cpp", @@ -34,6 +37,7 @@ cc_test { "DisplayConfigs_test.cpp", "DisplayEventReceiver_test.cpp", "EffectLayer_test.cpp", + "LayerBorder_test.cpp", "InvalidHandles_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", @@ -52,18 +56,15 @@ cc_test { "SetFrameRateOverride_test.cpp", "SetGeometry_test.cpp", "Stress_test.cpp", - "SurfaceInterceptor_test.cpp", "VirtualDisplay_test.cpp", "WindowInfosListener_test.cpp", ], data: ["SurfaceFlinger_test.filter"], static_libs: [ - "libtrace_proto", "liblayers_proto", "android.hardware.graphics.composer@2.1", ], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.common@1.2", "libandroid", "libbase", @@ -125,7 +126,6 @@ cc_test { } subdirs = [ - "fakehwc", "hwc2", "unittests", "utils", diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp index d70908e390..f2874ae0e1 100644 --- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp +++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp @@ -18,6 +18,7 @@ #include <gtest/gtest.h> +#include <gui/AidlStatusUtil.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> @@ -25,27 +26,34 @@ namespace android { +using gui::aidl_utils::statusTFromBinderStatus; + TEST(BootDisplayModeTest, setBootDisplayMode) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<gui::ISurfaceComposer> sf_aidl(ComposerServiceAIDL::getComposerService()); - auto displayToken = SurfaceComposerClient::getInternalDisplayToken(); + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); + + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); bool bootModeSupport = false; - binder::Status status = sf_aidl->getBootDisplayModeSupport(&bootModeSupport); - ASSERT_NO_FATAL_FAILURE(status.transactionError()); + binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport); + ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status)); if (bootModeSupport) { - ASSERT_EQ(NO_ERROR, sf->setBootDisplayMode(displayToken, 0)); + status = sf->setBootDisplayMode(displayToken, 0); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); } } TEST(BootDisplayModeTest, clearBootDisplayMode) { sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); - auto displayToken = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); bool bootModeSupport = false; binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport); - ASSERT_NO_FATAL_FAILURE(status.transactionError()); + ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status)); if (bootModeSupport) { status = sf->clearBootDisplayMode(displayToken); - ASSERT_EQ(NO_ERROR, status.transactionError()); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); } } diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp index 47a150dd35..d74bd55987 100644 --- a/services/surfaceflinger/tests/BufferGenerator.cpp +++ b/services/surfaceflinger/tests/BufferGenerator.cpp @@ -70,12 +70,13 @@ public: consumer->setDefaultBufferSize(width, height); consumer->setDefaultBufferFormat(format); - mBufferItemConsumer = new BufferItemConsumer(consumer, GraphicBuffer::USAGE_HW_TEXTURE); + mBufferItemConsumer = + sp<BufferItemConsumer>::make(consumer, GraphicBuffer::USAGE_HW_TEXTURE); - mListener = new BufferListener(consumer, callback); + mListener = sp<BufferListener>::make(consumer, callback); mBufferItemConsumer->setFrameAvailableListener(mListener); - mSurface = new Surface(producer, true); + mSurface = sp<Surface>::make(producer, true); } /* Used by Egl manager. The surface is never displayed. */ @@ -364,7 +365,7 @@ status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) *outBuffer = mGraphicBuffer; } if (outFence) { - *outFence = new Fence(mFence); + *outFence = sp<Fence>::make(mFence); } else { close(mFence); } diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 434c297cc9..4a45eb5586 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -18,13 +18,14 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <android/gui/ISurfaceComposer.h> #include <gtest/gtest.h> -#include <gui/ISurfaceComposer.h> +#include <gui/AidlStatusUtil.h> #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <private/android_filesystem_config.h> -#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <ui/DisplayMode.h> #include <ui/DynamicDisplayInfo.h> #include <utils/String8.h> @@ -34,6 +35,8 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; +using gui::LayerDebugInfo; +using gui::aidl_utils::statusTFromBinderStatus; using ui::ColorMode; namespace { @@ -67,12 +70,30 @@ protected: sp<SurfaceControl> mVirtualSurfaceControl; void initClient() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); } + static sp<IBinder> getFirstDisplayToken() { + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + if (ids.empty()) { + return nullptr; + } + + return SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + } + + static std::optional<uint64_t> getFirstDisplayId() { + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + if (ids.empty()) { + return std::nullopt; + } + + return ids.front().value; + } + void setupBackgroundSurface() { - mDisplay = SurfaceComposerClient::getInternalDisplayToken(); + mDisplay = getFirstDisplayToken(); ASSERT_FALSE(mDisplay == nullptr); ui::DisplayMode mode; @@ -149,45 +170,39 @@ TEST_F(CredentialsTest, ClientInitTest) { // Anyone else can init the client. { UIDFaker f(AID_BIN); - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_NO_FATAL_FAILURE(initClient()); } } TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { - std::function<bool()> condition = [] { - return SurfaceComposerClient::getInternalDisplayToken() != nullptr; - }; + std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; }; // Anyone can access display information. - ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true)); + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); } TEST_F(CredentialsTest, AllowedGetterMethodsTest) { // The following methods are tested with a UID that is not root, graphics, // or system, to show that anyone can access them. UIDFaker f(AID_BIN); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ASSERT_TRUE(display != nullptr); - - ui::DisplayMode mode; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - - Vector<ui::DisplayMode> modes; + const auto id = getFirstDisplayId(); + ASSERT_TRUE(id); ui::DynamicDisplayInfo info; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info)); } TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto id = getFirstDisplayId(); + ASSERT_TRUE(id); std::function<status_t()> condition = [=]() { ui::DynamicDisplayInfo info; - return SurfaceComposerClient::getDynamicDisplayInfo(display, &info); + return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR)); } TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = getFirstDisplayToken(); std::function<status_t()> condition = [=]() { ui::DisplayPrimaries primaries; return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries); @@ -196,30 +211,19 @@ TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) { } TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ui::DisplayModeId defaultMode; - bool allowGroupSwitching; - float primaryFpsMin; - float primaryFpsMax; - float appRequestFpsMin; - float appRequestFpsMax; - status_t res = - SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode, - &allowGroupSwitching, &primaryFpsMin, - &primaryFpsMax, &appRequestFpsMin, - &appRequestFpsMax); + const auto display = getFirstDisplayToken(); + gui::DisplayModeSpecs specs; + status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &specs); ASSERT_EQ(res, NO_ERROR); + gui::DisplayModeSpecs setSpecs; std::function<status_t()> condition = [=]() { - return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode, - allowGroupSwitching, primaryFpsMin, - primaryFpsMax, appRequestFpsMin, - appRequestFpsMax); + return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, specs); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); } TEST_F(CredentialsTest, SetActiveColorModeTest) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = getFirstDisplayToken(); std::function<status_t()> condition = [=]() { return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE); }; @@ -271,7 +275,7 @@ TEST_F(CredentialsTest, CreateDisplayTest) { } TEST_F(CredentialsTest, CaptureTest) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = getFirstDisplayToken(); std::function<status_t()> condition = [=]() { sp<GraphicBuffer> outBuffer; DisplayCaptureArgs captureArgs; @@ -301,39 +305,45 @@ TEST_F(CredentialsTest, CaptureLayersTest) { */ TEST_F(CredentialsTest, GetLayerDebugInfo) { setupBackgroundSurface(); - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); // Historically, only root and shell can access the getLayerDebugInfo which // is called when we call dumpsys. I don't see a reason why we should change this. std::vector<LayerDebugInfo> outLayers; + binder::Status status = binder::Status::ok(); // Check with root. { UIDFaker f(AID_ROOT); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + status = sf->getLayerDebugInfo(&outLayers); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); } // Check as a shell. { UIDFaker f(AID_SHELL); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + status = sf->getLayerDebugInfo(&outLayers); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); } // Check as anyone else. { UIDFaker f(AID_BIN); - ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); + status = sf->getLayerDebugInfo(&outLayers); + ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status)); } } TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = getFirstDisplayToken(); ASSERT_FALSE(display == nullptr); bool result = false; status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result); ASSERT_EQ(NO_ERROR, error); bool hasWideColorMode = false; + const auto id = getFirstDisplayId(); + ASSERT_TRUE(id); ui::DynamicDisplayInfo info; - SurfaceComposerClient::getDynamicDisplayInfo(display, &info); + SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info); const auto& colorModes = info.supportedColorModes; for (ColorMode colorMode : colorModes) { switch (colorMode) { @@ -350,7 +360,7 @@ TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { } TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = getFirstDisplayToken(); ASSERT_FALSE(display == nullptr); std::function<status_t()> condition = [=]() { bool result = false; @@ -360,10 +370,10 @@ TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) { } TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); + const auto id = getFirstDisplayId(); + ASSERT_TRUE(id); ui::DynamicDisplayInfo info; - SurfaceComposerClient::getDynamicDisplayInfo(display, &info); + SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info); ColorMode colorMode = info.activeColorMode; ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode); } diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp index 2dc96b8511..4be961bda1 100644 --- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp +++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp @@ -39,105 +39,71 @@ namespace android { */ class RefreshRateRangeTest : public ::testing::Test { private: - ui::DisplayModeId initialDefaultMode; - bool initialAllowGroupSwitching; - float initialPrimaryMin; - float initialPrimaryMax; - float initialAppRequestMin; - float initialAppRequestMax; + gui::DisplayModeSpecs mSpecs; protected: void SetUp() override { - mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); - status_t res = - SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, - &initialDefaultMode, - &initialAllowGroupSwitching, - &initialPrimaryMin, - &initialPrimaryMax, - &initialAppRequestMin, - &initialAppRequestMax); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + mDisplayId = ids.front().value; + mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs); ASSERT_EQ(res, NO_ERROR); } void TearDown() override { - status_t res = - SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode, - initialAllowGroupSwitching, - initialPrimaryMin, - initialPrimaryMax, - initialAppRequestMin, - initialAppRequestMax); + status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, mSpecs); ASSERT_EQ(res, NO_ERROR); } void testSetAllowGroupSwitching(bool allowGroupSwitching); sp<IBinder> mDisplayToken; + uint64_t mDisplayId; }; TEST_F(RefreshRateRangeTest, setAllConfigs) { ui::DynamicDisplayInfo info; - status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info); + status_t res = + SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId), + &info); const auto& modes = info.supportedDisplayModes; ASSERT_EQ(res, NO_ERROR); ASSERT_GT(modes.size(), 0); + gui::DisplayModeSpecs setSpecs; + setSpecs.allowGroupSwitching = false; for (size_t i = 0; i < modes.size(); i++) { - res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate); + setSpecs.defaultMode = modes[i].id; + setSpecs.primaryRanges.physical.min = modes[i].refreshRate; + setSpecs.primaryRanges.physical.max = modes[i].refreshRate; + setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; + setSpecs.appRequestRanges = setSpecs.primaryRanges; + res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); ASSERT_EQ(res, NO_ERROR); - ui::DisplayModeId defaultConfig; - bool allowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig, - &allowGroupSwitching, - &primaryRefreshRateMin, - &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); + gui::DisplayModeSpecs getSpecs; + res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(defaultConfig, i); - ASSERT_EQ(allowGroupSwitching, false); - ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate); - ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate); - ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate); - ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate); + ASSERT_EQ(setSpecs, getSpecs); } } void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) { - status_t res = - SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching, - 0.f, 90.f, 0.f, 90.f); + gui::DisplayModeSpecs setSpecs; + setSpecs.defaultMode = 0; + setSpecs.allowGroupSwitching = allowGroupSwitching; + setSpecs.primaryRanges.physical.min = 0; + setSpecs.primaryRanges.physical.max = 90; + setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; + setSpecs.appRequestRanges = setSpecs.primaryRanges; + + status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); ASSERT_EQ(res, NO_ERROR); - ui::DisplayModeId defaultConfig; - bool newAllowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig, - &newAllowGroupSwitching, - &primaryRefreshRateMin, - &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); + gui::DisplayModeSpecs getSpecs; + res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(defaultConfig, 0); - ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching); - ASSERT_EQ(primaryRefreshRateMin, 0.f); - ASSERT_EQ(primaryRefreshRateMax, 90.f); - ASSERT_EQ(appRequestRefreshRateMin, 0.f); - ASSERT_EQ(appRequestRefreshRateMax, 90.f); + ASSERT_EQ(setSpecs, getSpecs); } TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) { @@ -149,4 +115,4 @@ TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) { } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file +#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp index 0e54664f77..0df7e2fafa 100644 --- a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp +++ b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp @@ -36,7 +36,8 @@ TEST_F(DisplayEventReceiverTest, getLatestVsyncEventData) { EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now) << "Deadline timestamp should be greater than frame time"; for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) { - EXPECT_NE(FrameTimelineInfo::INVALID_VSYNC_ID, vsyncEventData.frameTimelines[i].vsyncId); + EXPECT_NE(gui::FrameTimelineInfo::INVALID_VSYNC_ID, + vsyncEventData.frameTimelines[i].vsyncId); EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime, vsyncEventData.frameTimelines[i].deadlineTimestamp) << "Expected vsync timestamp should be greater than deadline"; @@ -51,4 +52,4 @@ TEST_F(DisplayEventReceiverTest, getLatestVsyncEventData) { } } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp index 9fa0452915..52aa502743 100644 --- a/services/surfaceflinger/tests/EffectLayer_test.cpp +++ b/services/surfaceflinger/tests/EffectLayer_test.cpp @@ -28,7 +28,9 @@ protected: LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); mParentLayer = createColorLayer("Parent layer", Color::RED); @@ -177,7 +179,9 @@ TEST_F(EffectLayerTest, BlurEffectLayerIsVisible) { } TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ui::DisplayMode mode; ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); const ui::Size& resolution = mode.resolution; diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index ce94dab907..40a5d5757d 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -136,7 +136,7 @@ public: } status_t initClient() override { - mClient = new SurfaceComposerClient; + mClient = sp<SurfaceComposerClient>::make(); auto err = mClient->initCheck(); return err; } @@ -221,10 +221,12 @@ public: ProcessState::self()->startThreadPool(); } void SetUp() { - mClient = new SurfaceComposerClient; + mClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - mPrimaryDisplay = mClient->getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ui::DisplayMode mode; mClient->getActiveDisplayMode(mPrimaryDisplay, &mode); mDisplayWidth = mode.resolution.getWidth(); diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp index d192a2d83d..666ce76c6b 100644 --- a/services/surfaceflinger/tests/InvalidHandles_test.cpp +++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp @@ -42,13 +42,13 @@ protected: sp<SurfaceComposerClient> mScc; sp<SurfaceControl> mNotSc; void SetUp() override { - mScc = new SurfaceComposerClient; + mScc = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mScc->initCheck()); mNotSc = makeNotSurfaceControl(); } sp<SurfaceControl> makeNotSurfaceControl() { - return new SurfaceControl(mScc, new NotALayer(), nullptr, true); + return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1, "#1"); } }; @@ -64,4 +64,4 @@ TEST_F(InvalidHandleTest, captureLayersInvalidHandle) { } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file +#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp new file mode 100644 index 0000000000..00e134b4d2 --- /dev/null +++ b/services/surfaceflinger/tests/LayerBorder_test.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2022 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. + */ + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +// TODO: Amend all tests when screenshots become fully reworked for borders +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + +#include <chrono> // std::chrono::seconds +#include <thread> // std::this_thread::sleep_for +#include "LayerTransactionTest.h" + +namespace android { + +class LayerBorderTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + toHalf3 = ColorTransformHelper::toHalf3; + toHalf4 = ColorTransformHelper::toHalf4; + + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + ASSERT_FALSE(display == nullptr); + mColorOrange = toHalf4({255, 140, 0, 255}); + mParentLayer = createColorLayer("Parent layer", Color::RED); + + mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */, + 0 /* height */, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceContainer | + ISurfaceComposerClient::eNoColorFill, + mParentLayer->getHandle()); + EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer"; + + mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, + 0 /* height */, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceEffect | + ISurfaceComposerClient::eNoColorFill, + mContainerLayer->getHandle()); + EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1"; + + mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, + 0 /* height */, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceEffect | + ISurfaceComposerClient::eNoColorFill, + mContainerLayer->getHandle()); + + EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2"; + + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); + t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer); + t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); + + t.setColor(mEffectLayer1, toHalf3(Color::BLUE)); + + t.setColor(mEffectLayer2, toHalf3(Color::GREEN)); + }); + } + + virtual void TearDown() { + // Uncomment the line right below when running any of the tests + // std::this_thread::sleep_for (std::chrono::seconds(30)); + LayerTransactionTest::TearDown(); + mParentLayer = 0; + } + + std::function<half3(Color)> toHalf3; + std::function<half4(Color)> toHalf4; + sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2; + half4 mColorOrange; +}; + +TEST_F(LayerBorderTest, OverlappingVisibleRegions) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + + t.enableBorder(mEffectLayer1, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); + t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); + + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, EmptyVisibleRegion) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400)); + t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600)); + + t.enableBorder(mEffectLayer1, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, ZOrderAdjustment) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + t.setLayer(mParentLayer, 10); + t.setLayer(mEffectLayer1, 30); + t.setLayer(mEffectLayer2, 20); + + t.enableBorder(mEffectLayer1, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, GrandChildHierarchy) { + sp<SurfaceControl> containerLayer2 = + mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceContainer | + ISurfaceComposerClient::eNoColorFill, + mContainerLayer->getHandle()); + EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2"; + + sp<SurfaceControl> effectLayer3 = + mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceEffect | + ISurfaceComposerClient::eNoColorFill, + containerLayer2->getHandle()); + + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + t.setCrop(effectLayer3, Rect(400, 400, 800, 800)); + t.setColor(effectLayer3, toHalf3(Color::BLUE)); + + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(effectLayer3); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, TransparentAlpha) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + t.setAlpha(mEffectLayer1, 0.0f); + + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, SemiTransparentAlpha) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + t.setAlpha(mEffectLayer2, 0.5f); + + t.enableBorder(mEffectLayer2, true, 20, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, InvisibleLayers) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + t.hide(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, LayerWithBuffer) { + asTransaction([&](Transaction& t) { + t.hide(mEffectLayer1); + t.hide(mEffectLayer2); + t.show(mContainerLayer); + + sp<SurfaceControl> layer = + mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + mContainerLayer->getHandle()); + + sp<GraphicBuffer> buffer = + sp<GraphicBuffer>::make(400u, 400u, PIXEL_FORMAT_RGBA_8888, 1u, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | + BufferUsage::GPU_TEXTURE, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE); + + t.setBuffer(layer, buffer); + t.setPosition(layer, 100, 100); + t.show(layer); + t.enableBorder(mContainerLayer, true, 20, mColorOrange); + }); +} + +TEST_F(LayerBorderTest, CustomWidth) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + + t.enableBorder(mContainerLayer, true, 50, mColorOrange); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, CustomColor) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); + t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); + + t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255})); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) { + asTransaction([&](Transaction& t) { + t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); + t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); + + t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128})); + t.show(mEffectLayer1); + t.show(mEffectLayer2); + t.show(mContainerLayer); + }); +} + +} // namespace android + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 219db8c148..26dbc76c3c 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -51,7 +51,7 @@ public: LayerTransactionTest::TearDown(); } - virtual sp<SurfaceControl> createBufferStateLayer() { + virtual sp<SurfaceControl> createLayerWithBuffer() { return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState); } @@ -164,7 +164,7 @@ public: TEST_F(LayerCallbackTest, BufferColor) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -183,7 +183,7 @@ TEST_F(LayerCallbackTest, BufferColor) { TEST_F(LayerCallbackTest, NoBufferNoColor) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -206,7 +206,7 @@ TEST_F(LayerCallbackTest, NoBufferNoColor) { TEST_F(LayerCallbackTest, BufferNoColor) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -228,7 +228,7 @@ TEST_F(LayerCallbackTest, BufferNoColor) { TEST_F(LayerCallbackTest, NoBufferColor) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -266,7 +266,7 @@ TEST_F(LayerCallbackTest, NoStateChange) { TEST_F(LayerCallbackTest, OffScreen) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -288,8 +288,8 @@ TEST_F(LayerCallbackTest, OffScreen) { TEST_F(LayerCallbackTest, MergeBufferNoColor) { sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback1, callback2; @@ -322,8 +322,8 @@ TEST_F(LayerCallbackTest, MergeBufferNoColor) { TEST_F(LayerCallbackTest, MergeNoBufferColor) { sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback1, callback2; @@ -357,8 +357,8 @@ TEST_F(LayerCallbackTest, MergeNoBufferColor) { TEST_F(LayerCallbackTest, MergeOneBufferOneColor) { sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback1, callback2; @@ -392,8 +392,8 @@ TEST_F(LayerCallbackTest, MergeOneBufferOneColor) { } TEST_F(LayerCallbackTest, Merge_SameCallback) { sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback; @@ -418,7 +418,7 @@ TEST_F(LayerCallbackTest, Merge_SameCallback) { TEST_F(LayerCallbackTest, Merge_SameLayer) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback1, callback2; @@ -442,8 +442,8 @@ TEST_F(LayerCallbackTest, Merge_SameLayer) { } TEST_F(LayerCallbackTest, Merge_DifferentClients) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); + sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()), + client2(sp<SurfaceComposerClient>::make()); ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; @@ -485,7 +485,7 @@ TEST_F(LayerCallbackTest, Merge_DifferentClients) { TEST_F(LayerCallbackTest, MultipleTransactions) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -510,7 +510,7 @@ TEST_F(LayerCallbackTest, MultipleTransactions) { TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -541,7 +541,7 @@ TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) { TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -579,8 +579,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) { TEST_F(LayerCallbackTest, MultipleTransactions_Merge) { sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); Transaction transaction1, transaction2; CallbackHelper callback1, callback2; @@ -620,8 +620,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge) { } TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); + sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()), + client2(sp<SurfaceComposerClient>::make()); ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; @@ -669,8 +669,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) { } TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); + sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()), + client2(sp<SurfaceComposerClient>::make()); ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; @@ -730,8 +730,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateCha } TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); + sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()), + client2(sp<SurfaceComposerClient>::make()); ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; @@ -799,7 +799,7 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateC // TODO (b/183181768): Fix & re-enable TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -823,7 +823,7 @@ TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) { TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); // Normal call to set up test Transaction transaction; @@ -858,7 +858,7 @@ TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) { TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); // Normal call to set up test Transaction transaction; @@ -901,7 +901,7 @@ TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) { TEST_F(LayerCallbackTest, DesiredPresentTime) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -925,7 +925,7 @@ TEST_F(LayerCallbackTest, DesiredPresentTime) { TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback1; @@ -971,7 +971,7 @@ TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) { // TODO (b/183181768): Fix & re-enable TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback1; @@ -1015,7 +1015,7 @@ TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) { TEST_F(LayerCallbackTest, DesiredPresentTime_Past) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -1039,7 +1039,7 @@ TEST_F(LayerCallbackTest, DesiredPresentTime_Past) { TEST_F(LayerCallbackTest, ExpectedPresentTime) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -1050,7 +1050,10 @@ TEST_F(LayerCallbackTest, ExpectedPresentTime) { } const Vsync vsync = waitForNextVsync(); - transaction.setFrameTimelineInfo({vsync.vsyncId, 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = vsync.vsyncId; + ftInfo.inputEventId = 0; + transaction.setFrameTimelineInfo(ftInfo); transaction.apply(); ExpectedResult expected; @@ -1062,8 +1065,8 @@ TEST_F(LayerCallbackTest, ExpectedPresentTime) { // b202394221 TEST_F(LayerCallbackTest, EmptyBufferStateChanges) { sp<SurfaceControl> bufferLayer, emptyBufferLayer; - ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; @@ -1117,7 +1120,7 @@ TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) { TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); sp<SurfaceControl> offscreenLayer = createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, layer.get()); @@ -1148,7 +1151,7 @@ TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) { TEST_F(LayerCallbackTest, TransactionCommittedCallback_BSL) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer()); Transaction transaction; CallbackHelper callback; diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index 0e2bc3df0e..bf7cae9091 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -328,7 +328,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); ASSERT_NO_FATAL_FAILURE( TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT)); @@ -352,7 +352,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState shot->expectColor(bottom, Color::BLACK); } - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED)); ASSERT_NO_FATAL_FAILURE( @@ -399,7 +399,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_Buffe .apply(); ASSERT_NO_FATAL_FAILURE( fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layerR, Color::RED, 32, 32)); getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED); } @@ -482,7 +482,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) { } } -// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill +// RED: Color layer base color and Layer buffer fill // BLUE: prior background color // GREEN: final background color // BLACK: no color or fill @@ -516,7 +516,7 @@ void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType case ISurfaceComposerClient::eFXSurfaceBufferState: ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType)); if (bufferFill) { - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, fillColor, width, height)); expectedColor = fillColor; } Transaction().setCrop(layer, Rect(0, 0, width, height)).apply(); @@ -832,7 +832,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); const Rect crop(8, 8, 24, 24); Transaction().setCrop(layer, crop).apply(); @@ -863,7 +863,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); { SCOPED_TRACE("empty rect"); @@ -894,7 +894,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED); @@ -944,7 +944,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); const Rect crop(8, 8, 24, 24); Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply(); @@ -972,7 +972,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); const Rect frame(8, 8, 24, 24); Transaction t; @@ -988,7 +988,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); Transaction t; { @@ -1014,7 +1014,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 10, 10)); // A layer with a buffer will have a computed size that matches the buffer size. auto shot = getScreenCapture(); @@ -1026,11 +1026,11 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { sp<SurfaceControl> parent, child; ASSERT_NO_FATAL_FAILURE( parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE( child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10)); Transaction().reparent(child, parent).apply(); @@ -1047,7 +1047,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) { ASSERT_NO_FATAL_FAILURE( child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10)); Transaction().reparent(child, parent).apply(); @@ -1061,7 +1061,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); std::this_thread::sleep_for(500ms); @@ -1080,9 +1080,9 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) { child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState)); Transaction().reparent(child, parent).apply(); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10)); Rect childDst(0, 16, 32, 32); Transaction t; TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst); @@ -1099,7 +1099,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) { ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); auto shot = getScreenCapture(); shot->expectColor(Rect(0, 0, 32, 32), Color::RED); @@ -1111,7 +1111,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { ASSERT_NO_FATAL_FAILURE( layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); { SCOPED_TRACE("set buffer 1"); @@ -1120,7 +1120,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32)); { SCOPED_TRACE("set buffer 2"); @@ -1129,7 +1129,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); { SCOPED_TRACE("set buffer 3"); @@ -1148,7 +1148,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { ASSERT_NO_FATAL_FAILURE( layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::RED, 64, 64)); { SCOPED_TRACE("set layer 1 buffer red"); @@ -1156,7 +1156,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { shot->expectColor(Rect(0, 0, 64, 64), Color::RED); } - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::BLUE, 32, 32)); { SCOPED_TRACE("set layer 2 buffer blue"); @@ -1166,7 +1166,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { shot->expectColor(Rect(0, 32, 32, 64), Color::RED); } - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::GREEN, 64, 64)); { SCOPED_TRACE("set layer 1 buffer green"); auto shot = getScreenCapture(); @@ -1175,7 +1175,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); } - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::WHITE, 32, 32)); { SCOPED_TRACE("set layer 2 buffer white"); @@ -1197,7 +1197,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) { size_t idx = 0; for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); Color color = colors[idx % colors.size()]; TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); idx++; @@ -1230,7 +1230,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_Buffer size_t idx = 0; for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); Color color = colors[idx % colors.size()]; TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); idx++; @@ -1263,7 +1263,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferSt size_t idx = 0; for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); Color color = colors[idx % colors.size()]; TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); idx++; @@ -1344,7 +1344,7 @@ TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) { layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); sp<Fence> fence; @@ -1370,7 +1370,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); sp<Fence> fence = Fence::NO_FENCE; @@ -1388,7 +1388,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply(); @@ -1404,7 +1404,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); HdrMetadata hdrMetadata; @@ -1422,7 +1422,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); Region region; @@ -1440,7 +1440,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply(); diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp index 094b0ffc0f..21813700a2 100644 --- a/services/surfaceflinger/tests/LayerState_test.cpp +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -35,7 +35,7 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { args.frameScaleX = 2; args.frameScaleY = 4; args.captureSecureLayers = true; - args.displayToken = new BBinder(); + args.displayToken = sp<BBinder>::make(); args.width = 10; args.height = 20; args.useIdentityTransform = true; @@ -67,8 +67,8 @@ TEST(LayerStateTest, ParcellingLayerCaptureArgs) { args.frameScaleX = 2; args.frameScaleY = 4; args.captureSecureLayers = true; - args.layerHandle = new BBinder(); - args.excludeHandles = {new BBinder(), new BBinder()}; + args.layerHandle = sp<BBinder>::make(); + args.excludeHandles = {sp<BBinder>::make(), sp<BBinder>::make()}; args.childrenOnly = false; args.grayscale = true; @@ -90,13 +90,12 @@ TEST(LayerStateTest, ParcellingLayerCaptureArgs) { ASSERT_EQ(args.grayscale, args2.grayscale); } -TEST(LayerStateTest, ParcellingScreenCaptureResults) { +TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) { ScreenCaptureResults results; - results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0); - results.fence = new Fence(dup(fileno(tmpfile()))); + results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u); + results.fenceResult = sp<Fence>::make(dup(fileno(tmpfile()))); results.capturedSecureLayers = true; results.capturedDataspace = ui::Dataspace::DISPLAY_P3; - results.result = BAD_VALUE; Parcel p; results.writeToParcel(&p); @@ -110,10 +109,41 @@ TEST(LayerStateTest, ParcellingScreenCaptureResults) { ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth()); ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight()); ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat()); - ASSERT_EQ(results.fence->isValid(), results2.fence->isValid()); + ASSERT_TRUE(results.fenceResult.ok()); + ASSERT_TRUE(results2.fenceResult.ok()); + ASSERT_EQ(results.fenceResult.value()->isValid(), results2.fenceResult.value()->isValid()); ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers); ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace); - ASSERT_EQ(results.result, results2.result); +} + +TEST(LayerStateTest, ParcellingScreenCaptureResultsWithNoFenceOrError) { + ScreenCaptureResults results; + + Parcel p; + results.writeToParcel(&p); + p.setDataPosition(0); + + ScreenCaptureResults results2; + results2.readFromParcel(&p); + + ASSERT_TRUE(results2.fenceResult.ok()); + ASSERT_EQ(results2.fenceResult.value(), Fence::NO_FENCE); +} + +TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFenceError) { + ScreenCaptureResults results; + results.fenceResult = base::unexpected(BAD_VALUE); + + Parcel p; + results.writeToParcel(&p); + p.setDataPosition(0); + + ScreenCaptureResults results2; + results2.readFromParcel(&p); + + ASSERT_FALSE(results.fenceResult.ok()); + ASSERT_FALSE(results2.fenceResult.ok()); + ASSERT_EQ(results.fenceResult.error(), results2.fenceResult.error()); } } // namespace test diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index 6bd7920a62..badd5bebbc 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -23,9 +23,11 @@ #include <cutils/properties.h> #include <gtest/gtest.h> +#include <gui/AidlStatusUtil.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> #include <ui/DisplayMode.h> #include "BufferGenerator.h" @@ -39,13 +41,14 @@ using android::hardware::graphics::common::V1_1::BufferUsage; class LayerTransactionTest : public ::testing::Test { protected: void SetUp() override { - mClient = new SurfaceComposerClient; + mClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed)); + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); + binder::Status status = sf->getColorManagement(&mColorManagementUsed); + ASSERT_NO_FATAL_FAILURE(gui::aidl_utils::statusTFromBinderStatus(status)); mCaptureArgs.displayToken = mDisplay; } @@ -134,13 +137,16 @@ protected: postBufferQueueLayerBuffer(layer); } - virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { + virtual void fillBufferLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { sp<GraphicBuffer> buffer = - new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE, - "test"); + sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), + static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888, + 1u, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | + BufferUsage::GPU_TEXTURE, + "test"); TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); Transaction().setBuffer(layer, buffer).apply(); @@ -153,7 +159,7 @@ protected: fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); break; case ISurfaceComposerClient::eFXSurfaceBufferState: - fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight); + fillBufferLayerColor(layer, color, bufferWidth, bufferHeight); break; default: ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; @@ -206,10 +212,13 @@ protected: const Color& topRight, const Color& bottomLeft, const Color& bottomRight) { sp<GraphicBuffer> buffer = - new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE, - "test"); + sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), + static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888, + 1u, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | + BufferUsage::GPU_TEXTURE, + "test"); ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); @@ -224,7 +233,7 @@ protected: Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight); - Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply(); + Transaction().setBuffer(layer, buffer).apply(); } std::unique_ptr<ScreenCapture> screenshot() { @@ -280,7 +289,9 @@ protected: private: void SetUpDisplay() { - mDisplay = mClient->getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(mDisplay == nullptr) << "failed to get display"; ui::DisplayMode mode; diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp index ef992d6a40..cbd54e7aa9 100644 --- a/services/surfaceflinger/tests/LayerTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -32,7 +32,7 @@ TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) { Transaction().setTransformToDisplayInverse(layer, false).apply(); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::GREEN, 32, 32)); Transaction().setTransformToDisplayInverse(layer, true).apply(); } @@ -80,7 +80,7 @@ TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDropp sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); const auto producer = layer->getIGraphicBufferProducer(); - const sp<IProducerListener> stubListener(new StubProducerListener); + const sp<IProducerListener> stubListener(sp<StubProducerListener>::make()); IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); @@ -154,6 +154,36 @@ TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDropp ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU)); } + +// b/245052266 - we possible could support blur and a buffer at the same layer but +// might break existing assumptions at higher level. This test captures the current +// expectations. A layer drawing a buffer will not support blur. +TEST_F(LayerTransactionTest, BufferTakesPriorityOverBlur) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); + Transaction().setBackgroundBlurRadius(layer, 5).apply(); + { + SCOPED_TRACE("BufferTakesPriorityOverBlur"); + const Rect rect(0, 0, 32, 32); + auto shot = screenshot(); + shot->expectColor(rect, Color::RED); + } +} + +TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); + Transaction().setColor(layer, {Color::GREEN.r, Color::GREEN.g, Color::GREEN.b}).apply(); + { + SCOPED_TRACE("BufferTakesPriorityOverColor"); + const Rect rect(0, 0, 32, 32); + auto shot = screenshot(); + shot->expectColor(rect, Color::RED); + } +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 9cb617a980..f247c9f088 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -799,7 +799,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) { sp<Surface> surface = layer->getSurface(); sp<GraphicBuffer> buffer = - new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test"); + sp<GraphicBuffer>::make(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test"); ASSERT_NO_FATAL_FAILURE( TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT)); @@ -815,7 +815,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) { shot->expectColor(crop, Color::BLACK); } - buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); + buffer = sp<GraphicBuffer>::make(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test"); ASSERT_NO_FATAL_FAILURE( TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT)); diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp index e1a7ecc03b..867eddbc44 100644 --- a/services/surfaceflinger/tests/LayerUpdate_test.cpp +++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp @@ -33,7 +33,9 @@ protected: LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); ui::DisplayMode mode; diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index a921aa810e..e69db7c167 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -29,8 +29,10 @@ protected: virtual void SetUp() { LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); mParentLayer = createColorLayer("Parent layer", Color::RED); @@ -193,14 +195,14 @@ TEST_F(MirrorLayerTest, MirrorBufferLayer) { shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); } - sp<SurfaceControl> bufferStateLayer = - createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState, + sp<SurfaceControl> layer = + createLayer("Layer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState, mChildLayer.get()); - fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200); - Transaction().show(bufferStateLayer).apply(); + fillBufferLayerColor(layer, Color::BLUE, 200, 200); + Transaction().show(layer).apply(); { - SCOPED_TRACE("Initial Mirror BufferStateLayer"); + SCOPED_TRACE("Initial Mirror Layer"); auto shot = screenshot(); // Buffer mirror shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); @@ -208,9 +210,9 @@ TEST_F(MirrorLayerTest, MirrorBufferLayer) { shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); } - fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200); + fillBufferLayerColor(layer, Color::WHITE, 200, 200); { - SCOPED_TRACE("Update BufferStateLayer"); + SCOPED_TRACE("Update Layer"); auto shot = screenshot(); // Buffer mirror shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); @@ -218,9 +220,9 @@ TEST_F(MirrorLayerTest, MirrorBufferLayer) { shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); } - Transaction().reparent(bufferStateLayer, nullptr).apply(); + Transaction().reparent(layer, nullptr).apply(); { - SCOPED_TRACE("Removed BufferStateLayer"); + SCOPED_TRACE("Removed Layer"); auto shot = screenshot(); // Buffer mirror shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN); @@ -231,7 +233,10 @@ TEST_F(MirrorLayerTest, MirrorBufferLayer) { // Test that the mirror layer is initially offscreen. TEST_F(MirrorLayerTest, InitialMirrorState) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ui::DisplayMode mode; SurfaceComposerClient::getActiveDisplayMode(display, &mode); const ui::Size& size = mode.resolution; @@ -275,7 +280,9 @@ TEST_F(MirrorLayerTest, InitialMirrorState) { // Test that a mirror layer can be screenshot when offscreen TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ui::DisplayMode mode; SurfaceComposerClient::getActiveDisplayMode(display, &mode); const ui::Size& size = mode.resolution; @@ -283,7 +290,7 @@ TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) { sp<SurfaceControl> grandchild = createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState, mChildLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(grandchild, Color::BLUE, 50, 50)); Rect childBounds = Rect(50, 50, 450, 450); asTransaction([&](Transaction& t) { diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 1ed6c65afb..15ff696412 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -35,7 +35,9 @@ protected: LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - mMainDisplay = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState); SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode); diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp index 50a4092ddb..9cebf11b9c 100644 --- a/services/surfaceflinger/tests/RelativeZ_test.cpp +++ b/services/surfaceflinger/tests/RelativeZ_test.cpp @@ -33,7 +33,9 @@ protected: LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); // Back layer diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index a6d7f5834f..16076eaac9 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -110,10 +110,10 @@ public: } static sp<GraphicBuffer> getBuffer() { - return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); + return sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); } static uint64_t generateFrameNumber() { static uint64_t sFrameNumber = 0; @@ -332,8 +332,10 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { } TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { - sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener(); - sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener(); + sp<TransactionCompletedListener> firstCompletedListener = + sp<TransactionCompletedListener>::make(); + sp<TransactionCompletedListener> secondCompletedListener = + sp<TransactionCompletedListener>::make(); CallbackHelper callback1, callback2; @@ -433,8 +435,10 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) } TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) { - sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener(); - sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener(); + sp<TransactionCompletedListener> firstCompletedListener = + sp<TransactionCompletedListener>::make(); + sp<TransactionCompletedListener> secondCompletedListener = + sp<TransactionCompletedListener>::make(); TransactionCompletedListener::setInstance(firstCompletedListener); diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 6a7d8b8ca5..976ee3519c 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -30,7 +30,9 @@ protected: LayerTransactionTest::SetUp(); ASSERT_EQ(NO_ERROR, mClient->initCheck()); - const auto display = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + ASSERT_FALSE(ids.empty()); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ASSERT_FALSE(display == nullptr); ui::DisplayMode mode; @@ -371,7 +373,7 @@ TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) { ScreenCaptureResults captureResults; ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::RED, 32, 32)); SurfaceComposerClient::Transaction().apply(true); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults)); ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers); @@ -449,8 +451,8 @@ TEST_F(ScreenCaptureTest, CaptureCrop) { ISurfaceComposerClient::eFXSurfaceBufferState, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -484,8 +486,8 @@ TEST_F(ScreenCaptureTest, CaptureSize) { ISurfaceComposerClient::eFXSurfaceBufferState, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -519,7 +521,7 @@ TEST_F(ScreenCaptureTest, CaptureSize) { TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { LayerCaptureArgs args; - args.layerHandle = new BBinder(); + args.layerHandle = sp<BBinder>::make(); ScreenCaptureResults captureResults; // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND @@ -549,8 +551,8 @@ TEST_F(ScreenCaptureTest, CaptureSecureLayer) { ISurfaceComposerClient::eSecure | ISurfaceComposerClient::eFXSurfaceBufferState, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(secureLayer, Color::BLUE, 30, 30)); auto redLayerHandle = redLayer->getHandle(); Transaction() @@ -803,7 +805,7 @@ TEST_F(ScreenCaptureTest, CaptureWithGrayscale) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState, mBGSurfaceControl.get())); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); Transaction().show(layer).setLayer(layer, INT32_MAX).apply(); LayerCaptureArgs captureArgs; @@ -825,7 +827,7 @@ TEST_F(ScreenCaptureTest, CaptureWithGrayscale) { mCapture->expectColor(Rect(0, 0, 32, 32), Color{expectedColor, expectedColor, expectedColor, 255}, tolerance); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32)); ScreenCapture::captureLayers(&mCapture, captureArgs); expectedColor = luminance.b * 255; @@ -838,7 +840,7 @@ TEST_F(ScreenCaptureTest, CaptureOffscreen) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState, mBGSurfaceControl.get())); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32)); Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply(); @@ -865,7 +867,7 @@ TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState, mBGSurfaceControl.get())); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32)); Transaction() .show(layer) .setLayer(layer, INT32_MAX) @@ -885,7 +887,7 @@ TEST_F(ScreenCaptureTest, CaptureHdrLayer) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState, mBGSurfaceControl.get())); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32)); Transaction() .show(layer) .setLayer(layer, INT32_MAX) diff --git a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp index 4efec7738a..e43ef952d6 100644 --- a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp +++ b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ +#include <android/gui/ISurfaceComposer.h> #include <gtest/gtest.h> #include <gui/DisplayEventReceiver.h> -#include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <sys/epoll.h> #include <algorithm> @@ -24,12 +24,14 @@ namespace android { namespace { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; +using gui::ISurfaceComposer; class SetFrameRateOverrideTest : public ::testing::Test { protected: void SetUp() override { - const ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp; - const ISurfaceComposer::EventRegistrationFlags eventRegistration = { + const ISurfaceComposer::VsyncSource vsyncSource = + ISurfaceComposer::VsyncSource::eVsyncSourceApp; + const EventRegistrationFlags eventRegistration = { ISurfaceComposer::EventRegistration::frameRateOverride}; mDisplayEventReceiver = diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp index e9b6ba0f64..03201f7937 100644 --- a/services/surfaceflinger/tests/Stress_test.cpp +++ b/services/surfaceflinger/tests/Stress_test.cpp @@ -32,7 +32,7 @@ namespace android { TEST(SurfaceFlingerStress, create_and_destroy) { auto do_stress = []() { - sp<SurfaceComposerClient> client = new SurfaceComposerClient; + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, client->initCheck()); for (int j = 0; j < 1000; j++) { auto surf = client->createSurface(String8("t"), 100, 100, diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp deleted file mode 100644 index 28e8b8c78b..0000000000 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ /dev/null @@ -1,967 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - -#include <android-base/stringprintf.h> -#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h> -#include <google/protobuf/io/zero_copy_stream_impl.h> -#include <gtest/gtest.h> -#include <gui/ISurfaceComposer.h> -#include <gui/LayerState.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> -#include <private/gui/ComposerService.h> -#include <ui/DisplayMode.h> - -#include <fstream> -#include <random> -#include <thread> - -namespace android { - -using Transaction = SurfaceComposerClient::Transaction; -using SurfaceChange = surfaceflinger::SurfaceChange; -using Trace = surfaceflinger::Trace; -using Increment = surfaceflinger::Increment; - -constexpr uint32_t BUFFER_UPDATES = 18; -constexpr uint32_t LAYER_UPDATE = INT_MAX - 2; -constexpr uint32_t SIZE_UPDATE = 134; -constexpr uint32_t STACK_UPDATE = 1; -constexpr int32_t RELATIVE_Z = 42; -constexpr float ALPHA_UPDATE = 0.29f; -constexpr float CORNER_RADIUS_UPDATE = 0.2f; -constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24; -constexpr float POSITION_UPDATE = 121; -const Rect CROP_UPDATE(16, 16, 32, 32); -const float SHADOW_RADIUS_UPDATE = 35.0f; -std::vector<BlurRegion> BLUR_REGIONS_UPDATE; - -const String8 DISPLAY_NAME("SurfaceInterceptor Display Test"); -constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface"; -constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface"; -constexpr auto LAYER_NAME = "Layer Create and Delete Test"; - -constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope"; - -// Fill an RGBA_8888 formatted surface with a single color. -static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) { - ANativeWindow_Buffer outBuffer; - sp<Surface> s = sc->getSurface(); - ASSERT_TRUE(s != nullptr); - ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); - uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; y++) { - for (int x = 0; x < outBuffer.width; x++) { - uint8_t* pixel = img + (4 * (y*outBuffer.stride + x)); - pixel[0] = r; - pixel[1] = g; - pixel[2] = b; - pixel[3] = 255; - } - } - ASSERT_EQ(NO_ERROR, s->unlockAndPost()); -} - -static status_t readProtoFile(Trace* trace) { - status_t err = NO_ERROR; - - int fd = open(DEFAULT_FILENAME, O_RDONLY); - { - google::protobuf::io::FileInputStream f(fd); - if (fd && !trace->ParseFromZeroCopyStream(&f)) { - err = PERMISSION_DENIED; - } - } - close(fd); - - return err; -} - -static void enableInterceptor() { - system("service call SurfaceFlinger 1020 i32 1 > /dev/null"); -} - -static void disableInterceptor() { - system("service call SurfaceFlinger 1020 i32 0 > /dev/null"); -} - -std::string getUniqueName(const std::string& name, const Increment& increment) { - return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id()); -} - -int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) { - int32_t layerId = 0; - for (const auto& increment : capturedTrace.increment()) { - if (increment.increment_case() == increment.kSurfaceCreation) { - if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) { - layerId = increment.surface_creation().id(); - } - } - } - return layerId; -} - -int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) { - int32_t displayId = 0; - for (const auto& increment : capturedTrace.increment()) { - if (increment.increment_case() == increment.kDisplayCreation) { - if (increment.display_creation().name() == displayName) { - displayId = increment.display_creation().id(); - break; - } - } - } - return displayId; -} - -class SurfaceInterceptorTest : public ::testing::Test { -protected: - void SetUp() override { - // Allow SurfaceInterceptor write to /data - system("setenforce 0"); - - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - } - - void TearDown() override { - mComposerClient->dispose(); - mBGSurfaceControl.clear(); - mFGSurfaceControl.clear(); - mComposerClient.clear(); - system("setenforce 1"); - } - - sp<SurfaceComposerClient> mComposerClient; - sp<SurfaceControl> mBGSurfaceControl; - sp<SurfaceControl> mFGSurfaceControl; - int32_t mBGLayerId; - int32_t mFGLayerId; - -public: - using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&); - using TestAction = void (SurfaceInterceptorTest::*)(); - using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&); - using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&); - - void setupBackgroundSurface(); - void preProcessTrace(const Trace& trace); - - // captureTest will enable SurfaceInterceptor, setup background surface, - // disable SurfaceInterceptor, collect the trace and process the trace for - // id of background surface before further verification. - void captureTest(TestTransactionAction action, TestBooleanVerification verification); - void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase); - void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase); - void captureTest(TestAction action, TestBooleanVerification verification); - void captureTest(TestAction action, TestVerification verification); - void runInTransaction(TestTransactionAction action); - - // Verification of changes to a surface - bool positionUpdateFound(const SurfaceChange& change, bool foundPosition); - bool sizeUpdateFound(const SurfaceChange& change, bool foundSize); - bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha); - bool layerUpdateFound(const SurfaceChange& change, bool foundLayer); - bool cropUpdateFound(const SurfaceChange& change, bool foundCrop); - bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius); - bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change, - bool foundBackgroundBlurRadius); - bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions); - bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix); - bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode); - bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion); - bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack); - bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag); - bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag); - bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag); - bool reparentUpdateFound(const SurfaceChange& change, bool found); - bool relativeParentUpdateFound(const SurfaceChange& change, bool found); - bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found); - bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found); - bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase); - - // Find all of the updates in the single trace - void assertAllUpdatesFound(const Trace& trace); - - // Verification of creation and deletion of a surface - bool surfaceCreationFound(const Increment& increment, bool foundSurface); - bool surfaceDeletionFound(const Increment& increment, const int32_t targetId, - bool foundSurface); - bool displayCreationFound(const Increment& increment, bool foundDisplay); - bool displayDeletionFound(const Increment& increment, const int32_t targetId, - bool foundDisplay); - bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase); - - // Verification of buffer updates - bool bufferUpdatesFound(const Trace& trace); - - // Perform each of the possible changes to a surface - void positionUpdate(Transaction&); - void sizeUpdate(Transaction&); - void alphaUpdate(Transaction&); - void layerUpdate(Transaction&); - void cropUpdate(Transaction&); - void cornerRadiusUpdate(Transaction&); - void backgroundBlurRadiusUpdate(Transaction&); - void blurRegionsUpdate(Transaction&); - void matrixUpdate(Transaction&); - void transparentRegionHintUpdate(Transaction&); - void layerStackUpdate(Transaction&); - void hiddenFlagUpdate(Transaction&); - void opaqueFlagUpdate(Transaction&); - void secureFlagUpdate(Transaction&); - void reparentUpdate(Transaction&); - void relativeParentUpdate(Transaction&); - void shadowRadiusUpdate(Transaction&); - void trustedOverlayUpdate(Transaction&); - void surfaceCreation(Transaction&); - void displayCreation(Transaction&); - void displayDeletion(Transaction&); - - void nBufferUpdates(); - void runAllUpdates(); - -private: - void captureInTransaction(TestTransactionAction action, Trace*); - void capture(TestAction action, Trace*); -}; - -void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) { - enableInterceptor(); - setupBackgroundSurface(); - runInTransaction(action); - disableInterceptor(); - ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); - preProcessTrace(*outTrace); -} - -void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) { - enableInterceptor(); - setupBackgroundSurface(); - (this->*action)(); - disableInterceptor(); - ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); - preProcessTrace(*outTrace); -} - -void SurfaceInterceptorTest::setupBackgroundSurface() { - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); - - ui::DisplayMode mode; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - const ui::Size& resolution = mode.resolution; - - // Background surface - mBGSurfaceControl = - mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(), - resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mBGSurfaceControl != nullptr); - ASSERT_TRUE(mBGSurfaceControl->isValid()); - - // Foreground surface - mFGSurfaceControl = - mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(), - resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mFGSurfaceControl != nullptr); - ASSERT_TRUE(mFGSurfaceControl->isValid()); - - Transaction t; - t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - ASSERT_EQ(NO_ERROR, - t.setLayer(mBGSurfaceControl, INT_MAX - 3) - .show(mBGSurfaceControl) - .setLayer(mFGSurfaceControl, INT_MAX - 3) - .show(mFGSurfaceControl) - .apply()); -} - -void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) { - mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME); - mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME); -} - -void SurfaceInterceptorTest::captureTest(TestTransactionAction action, - TestBooleanVerification verification) { - Trace capturedTrace; - captureInTransaction(action, &capturedTrace); - ASSERT_TRUE((this->*verification)(capturedTrace)); -} - -void SurfaceInterceptorTest::captureTest(TestTransactionAction action, - Increment::IncrementCase incrementCase) { - Trace capturedTrace; - captureInTransaction(action, &capturedTrace); - ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase)); -} - -void SurfaceInterceptorTest::captureTest(TestTransactionAction action, - SurfaceChange::SurfaceChangeCase changeCase) { - Trace capturedTrace; - captureInTransaction(action, &capturedTrace); - ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase)); -} - -void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) { - Trace capturedTrace; - capture(action, &capturedTrace); - ASSERT_TRUE((this->*verification)(capturedTrace)); -} - -void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) { - Trace capturedTrace; - capture(action, &capturedTrace); - (this->*verification)(capturedTrace); -} - -void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) { - Transaction t; - (this->*action)(t); - t.apply(true); -} - -void SurfaceInterceptorTest::positionUpdate(Transaction& t) { - t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE); -} - -void SurfaceInterceptorTest::sizeUpdate(Transaction& t) { - t.setSize(mBGSurfaceControl, SIZE_UPDATE, SIZE_UPDATE); -} - -void SurfaceInterceptorTest::alphaUpdate(Transaction& t) { - t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE); -} - -void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) { - t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE); -} - -void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) { - t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE); -} - -void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) { - BLUR_REGIONS_UPDATE.empty(); - BLUR_REGIONS_UPDATE.push_back(BlurRegion()); - t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE); -} - -void SurfaceInterceptorTest::layerUpdate(Transaction& t) { - t.setLayer(mBGSurfaceControl, LAYER_UPDATE); -} - -void SurfaceInterceptorTest::cropUpdate(Transaction& t) { - t.setCrop(mBGSurfaceControl, CROP_UPDATE); -} - -void SurfaceInterceptorTest::matrixUpdate(Transaction& t) { - t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2); -} - -void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) { - Region region(CROP_UPDATE); - t.setTransparentRegionHint(mBGSurfaceControl, region); -} - -void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) { - t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE)); -} - -void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) { - t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); -} - -void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) { - t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); -} - -void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) { - t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure); -} - -void SurfaceInterceptorTest::reparentUpdate(Transaction& t) { - t.reparent(mBGSurfaceControl, mFGSurfaceControl); -} - -void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) { - t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z); -} - -void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) { - t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE); -} - -void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) { - t.setTrustedOverlay(mBGSurfaceControl, true); -} - -void SurfaceInterceptorTest::displayCreation(Transaction&) { - sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); - SurfaceComposerClient::destroyDisplay(testDisplay); -} - -void SurfaceInterceptorTest::displayDeletion(Transaction&) { - sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); - SurfaceComposerClient::destroyDisplay(testDisplay); -} - -void SurfaceInterceptorTest::runAllUpdates() { - runInTransaction(&SurfaceInterceptorTest::positionUpdate); - runInTransaction(&SurfaceInterceptorTest::sizeUpdate); - runInTransaction(&SurfaceInterceptorTest::alphaUpdate); - runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate); - runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate); - runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate); - runInTransaction(&SurfaceInterceptorTest::layerUpdate); - runInTransaction(&SurfaceInterceptorTest::cropUpdate); - runInTransaction(&SurfaceInterceptorTest::matrixUpdate); - runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate); - runInTransaction(&SurfaceInterceptorTest::layerStackUpdate); - runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate); - runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate); - runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate); - runInTransaction(&SurfaceInterceptorTest::reparentUpdate); - runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate); - runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate); - runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate); -} - -void SurfaceInterceptorTest::surfaceCreation(Transaction&) { - mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE, - PIXEL_FORMAT_RGBA_8888, 0); -} - -void SurfaceInterceptorTest::nBufferUpdates() { - std::random_device rd; - std::mt19937_64 gen(rd()); - // This makes testing fun - std::uniform_int_distribution<uint8_t> dis; - for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) { - fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen)); - } -} - -bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) { - // There should only be one position transaction with x and y = POSITION_UPDATE - bool hasX(change.position().x() == POSITION_UPDATE); - bool hasY(change.position().y() == POSITION_UPDATE); - if (hasX && hasY && !foundPosition) { - foundPosition = true; - } else if (hasX && hasY && foundPosition) { - // Failed because the position update was found a second time - [] () { FAIL(); }(); - } - return foundPosition; -} - -bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) { - bool hasWidth(change.size().h() == SIZE_UPDATE); - bool hasHeight(change.size().w() == SIZE_UPDATE); - if (hasWidth && hasHeight && !foundSize) { - foundSize = true; - } else if (hasWidth && hasHeight && foundSize) { - [] () { FAIL(); }(); - } - return foundSize; -} - -bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) { - bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE); - if (hasAlpha && !foundAlpha) { - foundAlpha = true; - } else if (hasAlpha && foundAlpha) { - [] () { FAIL(); }(); - } - return foundAlpha; -} - -bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change, - bool foundCornerRadius) { - bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE); - if (hasCornerRadius && !foundCornerRadius) { - foundCornerRadius = true; - } else if (hasCornerRadius && foundCornerRadius) { - [] () { FAIL(); }(); - } - return foundCornerRadius; -} - -bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change, - bool foundBackgroundBlur) { - bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() == - BACKGROUND_BLUR_RADIUS_UPDATE); - if (hasBackgroundBlur && !foundBackgroundBlur) { - foundBackgroundBlur = true; - } else if (hasBackgroundBlur && foundBackgroundBlur) { - []() { FAIL(); }(); - } - return foundBackgroundBlur; -} - -bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change, - bool foundBlurRegions) { - bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size()); - if (hasBlurRegions && !foundBlurRegions) { - foundBlurRegions = true; - } else if (hasBlurRegions && foundBlurRegions) { - []() { FAIL(); }(); - } - return foundBlurRegions; -} - -bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) { - bool hasLayer(change.layer().layer() == LAYER_UPDATE); - if (hasLayer && !foundLayer) { - foundLayer = true; - } else if (hasLayer && foundLayer) { - [] () { FAIL(); }(); - } - return foundLayer; -} - -bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) { - bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left); - bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top); - bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right); - bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom); - if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) { - foundCrop = true; - } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) { - [] () { FAIL(); }(); - } - return foundCrop; -} - -bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) { - bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2); - bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2); - bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2); - bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2); - if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) { - foundMatrix = true; - } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) { - [] () { FAIL(); }(); - } - return foundMatrix; -} - -bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change, - bool foundTransparentRegion) { - auto traceRegion = change.transparent_region_hint().region(0); - bool hasLeft(traceRegion.left() == CROP_UPDATE.left); - bool hasTop(traceRegion.top() == CROP_UPDATE.top); - bool hasRight(traceRegion.right() == CROP_UPDATE.right); - bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom); - if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) { - foundTransparentRegion = true; - } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) { - [] () { FAIL(); }(); - } - return foundTransparentRegion; -} - -bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change, - bool foundLayerStack) { - bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE); - if (hasLayerStackUpdate && !foundLayerStack) { - foundLayerStack = true; - } else if (hasLayerStackUpdate && foundLayerStack) { - [] () { FAIL(); }(); - } - return foundLayerStack; -} - -bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change, - bool foundHiddenFlag) { - bool hasHiddenFlag(change.hidden_flag().hidden_flag()); - if (hasHiddenFlag && !foundHiddenFlag) { - foundHiddenFlag = true; - } else if (hasHiddenFlag && foundHiddenFlag) { - [] () { FAIL(); }(); - } - return foundHiddenFlag; -} - -bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change, - bool foundOpaqueFlag) { - bool hasOpaqueFlag(change.opaque_flag().opaque_flag()); - if (hasOpaqueFlag && !foundOpaqueFlag) { - foundOpaqueFlag = true; - } else if (hasOpaqueFlag && foundOpaqueFlag) { - [] () { FAIL(); }(); - } - return foundOpaqueFlag; -} - -bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change, - bool foundSecureFlag) { - bool hasSecureFlag(change.secure_flag().secure_flag()); - if (hasSecureFlag && !foundSecureFlag) { - foundSecureFlag = true; - } else if (hasSecureFlag && foundSecureFlag) { - [] () { FAIL(); }(); - } - return foundSecureFlag; -} - -bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) { - bool hasId(change.reparent().parent_id() == mFGLayerId); - if (hasId && !found) { - found = true; - } else if (hasId && found) { - []() { FAIL(); }(); - } - return found; -} - -bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) { - bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId); - if (hasId && !found) { - found = true; - } else if (hasId && found) { - []() { FAIL(); }(); - } - return found; -} - -bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change, - bool foundShadowRadius) { - bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE); - if (hasShadowRadius && !foundShadowRadius) { - foundShadowRadius = true; - } else if (hasShadowRadius && foundShadowRadius) { - []() { FAIL(); }(); - } - return foundShadowRadius; -} - -bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change, - bool foundTrustedOverlay) { - bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay()); - if (hasTrustedOverlay && !foundTrustedOverlay) { - foundTrustedOverlay = true; - } else if (hasTrustedOverlay && foundTrustedOverlay) { - []() { FAIL(); }(); - } - return foundTrustedOverlay; -} - -bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, - SurfaceChange::SurfaceChangeCase changeCase) { - bool foundUpdate = false; - for (const auto& increment : trace.increment()) { - if (increment.increment_case() == increment.kTransaction) { - for (const auto& change : increment.transaction().surface_change()) { - if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) { - switch (changeCase) { - case SurfaceChange::SurfaceChangeCase::kPosition: - // foundUpdate is sent for the tests to fail on duplicated increments - foundUpdate = positionUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kSize: - foundUpdate = sizeUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kAlpha: - foundUpdate = alphaUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kLayer: - foundUpdate = layerUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kCrop: - foundUpdate = cropUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kCornerRadius: - foundUpdate = cornerRadiusUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius: - foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kBlurRegions: - foundUpdate = blurRegionsUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kMatrix: - foundUpdate = matrixUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: - foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kLayerStack: - foundUpdate = layerStackUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kHiddenFlag: - foundUpdate = hiddenFlagUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kOpaqueFlag: - foundUpdate = opaqueFlagUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kSecureFlag: - foundUpdate = secureFlagUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kReparent: - foundUpdate = reparentUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kRelativeParent: - foundUpdate = relativeParentUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kShadowRadius: - foundUpdate = shadowRadiusUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::kTrustedOverlay: - foundUpdate = trustedOverlayUpdateFound(change, foundUpdate); - break; - case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET: - break; - } - } - } - } - } - return foundUpdate; -} - -void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) { - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent)); -} - -bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) { - bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment)); - if (isMatch && !foundSurface) { - foundSurface = true; - } else if (isMatch && foundSurface) { - [] () { FAIL(); }(); - } - return foundSurface; -} - -bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, - const int32_t targetId, bool foundSurface) { - bool isMatch(increment.surface_deletion().id() == targetId); - if (isMatch && !foundSurface) { - foundSurface = true; - } else if (isMatch && foundSurface) { - [] () { FAIL(); }(); - } - return foundSurface; -} - -bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) { - bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() && - !increment.display_creation().is_secure()); - if (isMatch && !foundDisplay) { - foundDisplay = true; - } else if (isMatch && foundDisplay) { - [] () { FAIL(); }(); - } - return foundDisplay; -} - -bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, - const int32_t targetId, bool foundDisplay) { - bool isMatch(increment.display_deletion().id() == targetId); - if (isMatch && !foundDisplay) { - foundDisplay = true; - } else if (isMatch && foundDisplay) { - [] () { FAIL(); }(); - } - return foundDisplay; -} - -bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace, - Increment::IncrementCase incrementCase) { - bool foundIncrement = false; - for (const auto& increment : trace.increment()) { - if (increment.increment_case() == incrementCase) { - int32_t targetId = 0; - switch (incrementCase) { - case Increment::IncrementCase::kSurfaceCreation: - foundIncrement = surfaceCreationFound(increment, foundIncrement); - break; - case Increment::IncrementCase::kSurfaceDeletion: - // Find the id of created surface. - targetId = getSurfaceId(trace, LAYER_NAME); - foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement); - break; - case Increment::IncrementCase::kDisplayCreation: - foundIncrement = displayCreationFound(increment, foundIncrement); - break; - case Increment::IncrementCase::kDisplayDeletion: - // Find the id of created display. - targetId = getDisplayId(trace, DISPLAY_NAME.string()); - foundIncrement = displayDeletionFound(increment, targetId, foundIncrement); - break; - default: - /* code */ - break; - } - } - } - return foundIncrement; -} - -bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) { - uint32_t updates = 0; - for (const auto& inc : trace.increment()) { - if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) { - updates++; - } - } - return updates == BUFFER_UPDATES; -} - -TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) { - captureTest(&SurfaceInterceptorTest::positionUpdate, - SurfaceChange::SurfaceChangeCase::kPosition); -} - -TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) { - captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize); -} - -TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) { - captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha); -} - -TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) { - captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer); -} - -TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) { - captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop); -} - -TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) { - captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate, - SurfaceChange::SurfaceChangeCase::kCornerRadius); -} - -TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) { - captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate, - SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius); -} - -TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) { - captureTest(&SurfaceInterceptorTest::blurRegionsUpdate, - SurfaceChange::SurfaceChangeCase::kBlurRegions); -} - -TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { - captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); -} - -TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) { - captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate, - SurfaceChange::SurfaceChangeCase::kTransparentRegionHint); -} - -TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) { - captureTest(&SurfaceInterceptorTest::layerStackUpdate, - SurfaceChange::SurfaceChangeCase::kLayerStack); -} - -TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) { - captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate, - SurfaceChange::SurfaceChangeCase::kHiddenFlag); -} - -TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) { - captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate, - SurfaceChange::SurfaceChangeCase::kOpaqueFlag); -} - -TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) { - captureTest(&SurfaceInterceptorTest::secureFlagUpdate, - SurfaceChange::SurfaceChangeCase::kSecureFlag); -} - -TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) { - captureTest(&SurfaceInterceptorTest::reparentUpdate, - SurfaceChange::SurfaceChangeCase::kReparent); -} - -TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) { - captureTest(&SurfaceInterceptorTest::relativeParentUpdate, - SurfaceChange::SurfaceChangeCase::kRelativeParent); -} - -TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) { - captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate, - SurfaceChange::SurfaceChangeCase::kShadowRadius); -} - -TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) { - captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate, - SurfaceChange::SurfaceChangeCase::kTrustedOverlay); -} - -TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) { - captureTest(&SurfaceInterceptorTest::runAllUpdates, - &SurfaceInterceptorTest::assertAllUpdatesFound); -} - -TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) { - captureTest(&SurfaceInterceptorTest::surfaceCreation, - Increment::IncrementCase::kSurfaceCreation); -} - -TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) { - captureTest(&SurfaceInterceptorTest::displayCreation, - Increment::IncrementCase::kDisplayCreation); -} - -TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) { - enableInterceptor(); - runInTransaction(&SurfaceInterceptorTest::displayDeletion); - disableInterceptor(); - Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion)); -} - -// If the interceptor is enabled while buffer updates are being pushed, the interceptor should -// first create a snapshot of the existing displays and surfaces and then start capturing -// the buffer updates -TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) { - setupBackgroundSurface(); - std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this); - enableInterceptor(); - disableInterceptor(); - bufferUpdates.join(); - - Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - const auto& firstIncrement = capturedTrace.mutable_increment(0); - ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation); -} -} -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index 8ce63bc64c..797a64c7d7 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -35,7 +35,10 @@ public: return mDelegate->screenshot(); case RenderPath::VIRTUAL_DISPLAY: - const auto displayToken = SurfaceComposerClient::getInternalDisplayToken(); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + const auto displayToken = ids.empty() + ? nullptr + : SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); ui::DisplayState displayState; SurfaceComposerClient::getDisplayState(displayToken, &displayState); @@ -53,11 +56,11 @@ public: consumer->setConsumerName(String8("Virtual disp consumer")); consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); - itemConsumer = new BufferItemConsumer(consumer, - // Sample usage bits from screenrecord - GRALLOC_USAGE_HW_VIDEO_ENCODER | - GRALLOC_USAGE_SW_READ_OFTEN); - sp<BufferListener> listener = new BufferListener(this); + itemConsumer = sp<BufferItemConsumer>::make(consumer, + // Sample usage bits from screenrecord + GRALLOC_USAGE_HW_VIDEO_ENCODER | + GRALLOC_USAGE_SW_READ_OFTEN); + sp<BufferListener> listener = sp<BufferListener>::make(this); itemConsumer->setFrameAvailableListener(listener); vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp index 18e08062c0..f31f582ffd 100644 --- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp +++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp @@ -33,7 +33,7 @@ protected: consumer->setConsumerName(String8("Virtual disp consumer")); consumer->setDefaultBufferSize(100, 100); - mGLConsumer = new GLConsumer(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false); + mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false); } sp<IGraphicBufferProducer> mProducer; @@ -55,7 +55,7 @@ TEST_F(VirtualDisplayTest, VirtualDisplayDestroyedSurfaceReuse) { // add another sync since we are deferring the display destruction t.apply(true); - sp<Surface> surface = new Surface(mProducer); + sp<Surface> surface = sp<Surface>::make(mProducer); sp<ANativeWindow> window(surface); ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_EGL)); diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index bb522451a6..53c3c3998f 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -29,8 +29,8 @@ class WindowInfosListenerTest : public ::testing::Test { protected: void SetUp() override { seteuid(AID_SYSTEM); - mClient = new SurfaceComposerClient; - mWindowInfosListener = new SyncWindowInfosListener(); + mClient = sp<SurfaceComposerClient>::make(); + mWindowInfosListener = sp<SyncWindowInfosListener>::make(); mClient->addWindowInfosListener(mWindowInfosListener); } @@ -77,7 +77,7 @@ std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo, TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { std::string name = "Test Layer"; - sp<IBinder> token = new BBinder(); + sp<IBinder> token = sp<BBinder>::make(); WindowInfo windowInfo; windowInfo.name = name; windowInfo.token = token; @@ -105,7 +105,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { TEST_F(WindowInfosListenerTest, WindowInfoChanged) { std::string name = "Test Layer"; - sp<IBinder> token = new BBinder(); + sp<IBinder> token = sp<BBinder>::make(); WindowInfo windowInfo; windowInfo.name = name; windowInfo.token = token; diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp deleted file mode 100644 index 704815de3a..0000000000 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ /dev/null @@ -1,63 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -cc_test { - name: "sffakehwc_test", - defaults: ["surfaceflinger_defaults"], - test_suites: ["device-tests"], - srcs: [ - "FakeComposerClient.cpp", - "FakeComposerService.cpp", - "FakeComposerUtils.cpp", - "SFFakeHwc_test.cpp", - ], - require_root: true, - shared_libs: [ - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.composer@2.3", - "android.hardware.graphics.composer@2.4", - "android.hardware.graphics.composer3-V1-ndk", - "android.hardware.graphics.mapper@2.0", - "android.hardware.graphics.mapper@3.0", - "android.hardware.graphics.mapper@4.0", - "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", - "libbase", - "libbinder", - "libbinder_ndk", - "libcutils", - "libfmq", - "libgui", - "libhidlbase", - "liblayers_proto", - "liblog", - "libnativewindow", - "libsync", - "libtimestats", - "libui", - "libutils", - ], - static_libs: [ - "android.hardware.graphics.composer@2.1-resources", - "libaidlcommonsupport", - "libcompositionengine", - "libgmock", - "libperfetto_client_experimental", - "librenderengine", - "libtrace_proto", - "libaidlcommonsupport", - ], - header_libs: [ - "android.hardware.graphics.composer@2.4-command-buffer", - "android.hardware.graphics.composer@2.4-hal", - "android.hardware.graphics.composer3-command-buffer", - "libsurfaceflinger_headers", - ], -} diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp deleted file mode 100644 index b38032d265..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp +++ /dev/null @@ -1,927 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -//#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "FakeComposer" - -#include "FakeComposerClient.h" - -#include <gui/SurfaceComposerClient.h> - -#include <log/log.h> - -#include <gtest/gtest.h> - -#include <inttypes.h> -#include <time.h> -#include <algorithm> -#include <condition_variable> -#include <iostream> -#include <mutex> -#include <set> -#include <thread> - -constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0); - -using namespace sftest; - -using android::Condition; -using android::Mutex; - -using Clock = std::chrono::steady_clock; -using TimePoint = std::chrono::time_point<Clock>; - -namespace { - -// Internal state of a layer in the HWC API. -class LayerImpl { -public: - LayerImpl() = default; - - bool mValid = true; - RenderState mRenderState; - uint32_t mZ = 0; -}; - -// Struct for storing per frame rectangle state. Contains the render -// state shared to the test case. Basically a snapshot and a subset of -// LayerImpl sufficient to re-create the pixels of a layer for the -// frame. -struct FrameRect { -public: - FrameRect(Layer layer_, const RenderState& state, uint32_t z_) - : layer(layer_), renderState(state), z(z_) {} - - const Layer layer; - const RenderState renderState; - const uint32_t z; -}; - -// Collection of FrameRects forming one rendered frame. Could store -// related fences and other data in the future. -class Frame { -public: - Frame() = default; - std::vector<std::unique_ptr<FrameRect>> rectangles; -}; - -class DelayedEventGenerator { -public: - explicit DelayedEventGenerator(std::function<void()> onTimerExpired) - : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {} - - ~DelayedEventGenerator() { - ALOGI("DelayedEventGenerator exiting."); - { - std::unique_lock<std::mutex> lock(mMutex); - mRunning = false; - mWakeups.clear(); - mCondition.notify_one(); - } - mThread.join(); - ALOGI("DelayedEventGenerator exited."); - } - - void wakeAfter(std::chrono::nanoseconds waitTime) { - std::unique_lock<std::mutex> lock(mMutex); - mWakeups.insert(Clock::now() + waitTime); - mCondition.notify_one(); - } - -private: - void loop() { - while (true) { - // Lock scope - { - std::unique_lock<std::mutex> lock(mMutex); - mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); }); - if (!mRunning && mWakeups.empty()) { - // This thread should only exit once the destructor has been called and all - // wakeups have been processed - return; - } - - // At this point, mWakeups will not be empty - - TimePoint target = *(mWakeups.begin()); - auto status = mCondition.wait_until(lock, target); - while (status == std::cv_status::no_timeout) { - // This was either a spurious wakeup or another wakeup was added, so grab the - // oldest point and wait again - target = *(mWakeups.begin()); - status = mCondition.wait_until(lock, target); - } - - // status must have been timeout, so we can finally clear this point - mWakeups.erase(target); - } - // Callback *without* locks! - mOnTimerExpired(); - } - } - - std::function<void()> mOnTimerExpired; - std::thread mThread; - std::mutex mMutex; - std::condition_variable mCondition; - bool mRunning = true; - std::set<TimePoint> mWakeups; -}; - -} // namespace - -FakeComposerClient::FakeComposerClient() - : mEventCallback(nullptr), - mEventCallback_2_4(nullptr), - mCurrentConfig(NULL_DISPLAY_CONFIG), - mVsyncEnabled(false), - mLayers(), - mDelayedEventGenerator( - std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })), - mSurfaceComposer(nullptr) {} - -FakeComposerClient::~FakeComposerClient() {} - -bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) { - return false; -} - -std::string FakeComposerClient::dumpDebugInfo() { - return {}; -} - -void FakeComposerClient::registerEventCallback(EventCallback* callback) { - ALOGV("registerEventCallback"); - LOG_FATAL_IF(mEventCallback_2_4 != nullptr, - "already registered using registerEventCallback_2_4"); - - mEventCallback = callback; - if (mEventCallback) { - mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED); - } -} - -void FakeComposerClient::unregisterEventCallback() { - ALOGV("unregisterEventCallback"); - mEventCallback = nullptr; -} - -void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) { - if (mEventCallback) { - mEventCallback->onHotplug(display, state); - } else if (mEventCallback_2_4) { - mEventCallback_2_4->onHotplug(display, state); - } -} - -void FakeComposerClient::refreshDisplay(Display display) { - if (mEventCallback) { - mEventCallback->onRefresh(display); - } else if (mEventCallback_2_4) { - mEventCallback_2_4->onRefresh(display); - } -} - -uint32_t FakeComposerClient::getMaxVirtualDisplayCount() { - ALOGV("getMaxVirtualDisplayCount"); - return 1; -} - -V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, - V1_0::PixelFormat* /*format*/, - Display* /*outDisplay*/) { - ALOGV("createVirtualDisplay"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) { - ALOGV("destroyVirtualDisplay"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) { - ALOGV("createLayer"); - *outLayer = mLayers.size(); - auto newLayer = std::make_unique<LayerImpl>(); - mLayers.push_back(std::move(newLayer)); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) { - ALOGV("destroyLayer"); - mLayers[layer]->mValid = false; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) { - ALOGV("getActiveConfig"); - if (mMockHal) { - return mMockHal->getActiveConfig(display, outConfig); - } - - // TODO Assert outConfig != nullptr - - // TODO This is my reading of the - // IComposerClient::getActiveConfig, but returning BAD_CONFIG - // seems to not fit SurfaceFlinger plans. See version 2 below. - // if (mCurrentConfig == NULL_DISPLAY_CONFIG) { - // return V2_1::Error::BAD_CONFIG; - // } - //*outConfig = mCurrentConfig; - *outConfig = 1; // Very special config for you my friend - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/, - uint32_t /*height*/, - V1_0::PixelFormat /*format*/, - V1_0::Dataspace /*dataspace*/) { - ALOGV("getClientTargetSupport"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getColorModes(Display /*display*/, - hidl_vec<V1_0::ColorMode>* /*outModes*/) { - ALOGV("getColorModes"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config, - V2_1::IComposerClient::Attribute attribute, - int32_t* outValue) { - auto tmpError = - getDisplayAttribute_2_4(display, config, - static_cast<IComposerClient::Attribute>(attribute), outValue); - return static_cast<V2_1::Error>(tmpError); -} - -V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) { - ALOGV("getDisplayConfigs"); - if (mMockHal) { - return mMockHal->getDisplayConfigs(display, outConfigs); - } - - // TODO assert display == 1, outConfigs != nullptr - - outConfigs->resize(1); - (*outConfigs)[0] = 1; - - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) { - ALOGV("getDisplayName"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/, - IComposerClient::DisplayType* outType) { - ALOGV("getDisplayType"); - // TODO: This setting nothing on the output had no effect on initial trials. Is first display - // assumed to be physical? - *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) { - ALOGV("getDozeSupport"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/, - hidl_vec<V1_0::Hdr>* /*outTypes*/, - float* /*outMaxLuminance*/, - float* /*outMaxAverageLuminance*/, - float* /*outMinLuminance*/) { - ALOGV("getHdrCapabilities"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) { - ALOGV("setActiveConfig"); - if (mMockHal) { - return mMockHal->setActiveConfig(display, config); - } - mCurrentConfig = config; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) { - ALOGV("setColorMode"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/, - V2_1::IComposerClient::PowerMode /*mode*/) { - ALOGV("setPowerMode"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/, - IComposerClient::Vsync enabled) { - mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE); - ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/, - int32_t /*hint*/) { - ALOGV("setColorTransform"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/, - int32_t /*acquireFence*/, int32_t /*dataspace*/, - const std::vector<hwc_rect_t>& /*damage*/) { - ALOGV("setClientTarget"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/, - int32_t /*releaseFence*/) { - ALOGV("setOutputBuffer"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::validateDisplay( - Display /*display*/, std::vector<Layer>* /*outChangedLayers*/, - std::vector<IComposerClient::Composition>* /*outCompositionTypes*/, - uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/, - std::vector<uint32_t>* /*outRequestMasks*/) { - ALOGV("validateDisplay"); - // TODO: Assume touching nothing means All Korrekt! - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) { - ALOGV("acceptDisplayChanges"); - // Didn't ask for changes because software is omnipotent. - return V2_1::Error::NONE; -} - -bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) { - return a->z <= b->z; -} - -V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/, - std::vector<Layer>* /*outLayers*/, - std::vector<int32_t>* /*outReleaseFences*/) { - ALOGV("presentDisplay"); - // TODO Leaving layers and their fences out for now. Doing so - // means that we've already processed everything. Important to - // test that the fences are respected, though. (How?) - - std::unique_ptr<Frame> newFrame(new Frame); - for (uint64_t layer = 0; layer < mLayers.size(); layer++) { - const LayerImpl& layerImpl = *mLayers[layer]; - - if (!layerImpl.mValid) continue; - - auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ); - newFrame->rectangles.push_back(std::move(rect)); - } - std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering); - { - Mutex::Autolock _l(mStateMutex); - mFrames.push_back(std::move(newFrame)); - mFramesAvailable.broadcast(); - } - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/, - int32_t /*x*/, int32_t /*y*/) { - ALOGV("setLayerCursorPosition"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, - buffer_handle_t buffer, int32_t acquireFence) { - ALOGV("setLayerBuffer"); - LayerImpl& l = getLayerImpl(layer); - if (buffer != l.mRenderState.mBuffer) { - l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not? - } - l.mRenderState.mBuffer = buffer; - l.mRenderState.mAcquireFence = acquireFence; - - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/, - const std::vector<hwc_rect_t>& /*damage*/) { - ALOGV("setLayerSurfaceDamage"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) { - ALOGV("setLayerBlendMode"); - getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer, - IComposerClient::Color color) { - ALOGV("setLayerColor"); - getLayerImpl(layer).mRenderState.mLayerColor.r = color.r; - getLayerImpl(layer).mRenderState.mLayerColor.g = color.g; - getLayerImpl(layer).mRenderState.mLayerColor.b = color.b; - getLayerImpl(layer).mRenderState.mLayerColor.a = color.a; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/, - int32_t /*type*/) { - ALOGV("setLayerCompositionType"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/, - int32_t /*dataspace*/) { - ALOGV("setLayerDataspace"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer, - const hwc_rect_t& frame) { - ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right, - frame.bottom); - getLayerImpl(layer).mRenderState.mDisplayFrame = frame; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) { - ALOGV("setLayerPlaneAlpha"); - getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/, - buffer_handle_t /*stream*/) { - ALOGV("setLayerSidebandStream"); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer, - const hwc_frect_t& crop) { - ALOGV("setLayerSourceCrop"); - getLayerImpl(layer).mRenderState.mSourceCrop = crop; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, - int32_t transform) { - ALOGV("setLayerTransform"); - getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform); - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer, - const std::vector<hwc_rect_t>& visible) { - ALOGV("setLayerVisibleRegion"); - getLayerImpl(layer).mRenderState.mVisibleRegion = visible; - return V2_1::Error::NONE; -} - -V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) { - ALOGV("setLayerZOrder"); - getLayerImpl(layer).mZ = z; - return V2_1::Error::NONE; -} - -// Composer 2.2 -V2_1::Error FakeComposerClient::getPerFrameMetadataKeys( - Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setLayerPerFrameMetadata( - Display /*display*/, Layer /*layer*/, - const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getReadbackBufferAttributes( - Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/, - graphics::common::V1_1::Dataspace* /*outDataspace*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/, - const native_handle_t* /*bufferHandle*/, - android::base::unique_fd /*fenceFd*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/, - android::base::unique_fd* /*outFenceFd*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::createVirtualDisplay_2_2( - uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/, - Display* /*outDisplay*/) { - return V2_1::Error::UNSUPPORTED; -} -V2_1::Error FakeComposerClient::getClientTargetSupport_2_2( - Display /*display*/, uint32_t /*width*/, uint32_t /*height*/, - graphics::common::V1_1::PixelFormat /*format*/, - graphics::common::V1_1::Dataspace /*dataspace*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/, - V2_2::IComposerClient::PowerMode /*mode*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/, - V2_2::IComposerClient::FloatColor /*color*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getColorModes_2_2( - Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getRenderIntents( - Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/, - std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/, - graphics::common::V1_1::ColorMode /*mode*/, - graphics::common::V1_1::RenderIntent /*intent*/) { - return V2_1::Error::UNSUPPORTED; -} - -std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix( - graphics::common::V1_1::Dataspace /*dataspace*/) { - return {}; -} - -// Composer 2.3 -V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3( - Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/, - graphics::common::V1_2::ColorMode /*mode*/, - graphics::common::V1_1::RenderIntent /*intent*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getRenderIntents_2_3( - Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/, - std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getColorModes_2_3( - Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getClientTargetSupport_2_3( - Display /*display*/, uint32_t /*width*/, uint32_t /*height*/, - graphics::common::V1_2::PixelFormat /*format*/, - graphics::common::V1_2::Dataspace /*dataspace*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3( - Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/, - graphics::common::V1_2::Dataspace* /*outDataspace*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getHdrCapabilities_2_3( - Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/, - float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3( - Display /*display*/, Layer /*layer*/, - const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/, - uint8_t* /*outPort*/, - std::vector<uint8_t>* /*outData*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/, - const float* /*matrix*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes( - uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/, - graphics::common::V1_2::Dataspace& /*dataspace*/, - hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled( - uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/, - hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/, - uint64_t /*maxFrames*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getDisplayedContentSample( - uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/, - uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/, - hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/, - hidl_vec<uint64_t>& /*sampleComponent3*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getDisplayCapabilities( - Display /*display*/, - std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs( - Display /*display*/, Layer /*layer*/, - std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/, - bool* /*outSupport*/) { - return V2_1::Error::UNSUPPORTED; -} - -V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) { - return V2_1::Error::UNSUPPORTED; -} - -// Composer 2.4 -void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) { - ALOGV("registerEventCallback_2_4"); - LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback"); - - mEventCallback_2_4 = callback; - if (mEventCallback_2_4) { - mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED); - } -} - -void FakeComposerClient::unregisterEventCallback_2_4() { - ALOGV("unregisterEventCallback_2_4"); - mEventCallback_2_4 = nullptr; -} - -V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4( - Display /*display*/, - std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) { - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::getDisplayConnectionType( - Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) { - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) { - ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display), - static_cast<int>(config), static_cast<int>(attribute), outValue); - if (mMockHal) { - return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue); - } - - // TODO: SOOO much fun to be had with these alone - switch (attribute) { - case IComposerClient::Attribute::WIDTH: - *outValue = 1920; - break; - case IComposerClient::Attribute::HEIGHT: - *outValue = 1080; - break; - case IComposerClient::Attribute::VSYNC_PERIOD: - *outValue = 1666666666; - break; // TOOD: Tests break down if lowered to 16ms? - case IComposerClient::Attribute::DPI_X: - *outValue = 240; - break; - case IComposerClient::Attribute::DPI_Y: - *outValue = 240; - break; - default: - LOG_ALWAYS_FATAL("Say what!?! New attribute"); - } - - return Error::NONE; -} - -V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display, - V2_4::VsyncPeriodNanos* outVsyncPeriod) { - ALOGV("getDisplayVsyncPeriod"); - if (mMockHal) { - return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod); - } - - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::setActiveConfigWithConstraints( - Display display, Config config, - const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, - VsyncPeriodChangeTimeline* timeline) { - ALOGV("setActiveConfigWithConstraints"); - if (mMockHal) { - return mMockHal->setActiveConfigWithConstraints(display, config, - vsyncPeriodChangeConstraints, timeline); - } - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) { - ALOGV("setAutoLowLatencyMode"); - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::getSupportedContentTypes( - Display, std::vector<IComposerClient::ContentType>*) { - ALOGV("getSupportedContentTypes"); - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) { - ALOGV("setContentType"); - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::validateDisplay_2_4( - Display /*display*/, std::vector<Layer>* /*outChangedLayers*/, - std::vector<IComposerClient::Composition>* /*outCompositionTypes*/, - uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/, - std::vector<uint32_t>* /*outRequestMasks*/, - IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) { - return V2_4::Error::NONE; -} - -V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool, - const std::vector<uint8_t>&) { - ALOGV("setLayerGenericMetadata"); - return V2_4::Error::UNSUPPORTED; -} - -V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys( - std::vector<IComposerClient::LayerGenericMetadataKey>*) { - ALOGV("getLayerGenericMetadataKeys"); - return V2_4::Error::UNSUPPORTED; -} - -////////////////////////////////////////////////////////////////// - -void FakeComposerClient::requestVSync(uint64_t vsyncTime) { - if (mEventCallback || mEventCallback_2_4) { - uint64_t timestamp = vsyncTime; - ALOGV("Vsync"); - if (timestamp == 0) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; - } - if (mSurfaceComposer != nullptr) { - mSurfaceComposer->injectVSync(timestamp); - } else if (mEventCallback) { - mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp); - } else { - mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666); - } - } -} - -void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) { - mDelayedEventGenerator->wakeAfter(wait); -} - -LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) { - // TODO Change these to an internal state check that can be - // invoked from the gtest? GTest macros do not seem all that safe - // when used outside the test class - EXPECT_GE(handle, static_cast<Layer>(0)); - EXPECT_LT(handle, mLayers.size()); - return *(mLayers[handle]); -} - -int FakeComposerClient::getFrameCount() const { - return mFrames.size(); -} - -static std::vector<RenderState> extractRenderState( - const std::vector<std::unique_ptr<FrameRect>>& internalRects) { - std::vector<RenderState> result; - result.reserve(internalRects.size()); - for (const std::unique_ptr<FrameRect>& rect : internalRects) { - result.push_back(rect->renderState); - } - return result; -} - -std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const { - Mutex::Autolock _l(mStateMutex); - return extractRenderState(mFrames[frame]->rectangles); -} - -std::vector<RenderState> FakeComposerClient::getLatestFrame() const { - Mutex::Autolock _l(mStateMutex); - return extractRenderState(mFrames[mFrames.size() - 1]->rectangles); -} - -void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) { - int currentFrame = 0; - { - Mutex::Autolock _l(mStateMutex); // I hope this is ok... - currentFrame = static_cast<int>(mFrames.size()); - requestVSync(); - } - waitUntilFrame(currentFrame + 1, maxWait); -} - -void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const { - Mutex::Autolock _l(mStateMutex); - while (mFrames.size() < static_cast<size_t>(targetFrame)) { - android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count()); - if (result == android::TIMED_OUT) { - ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame, - mFrames.size(), maxWait.count()); - return; - } - } -} - -void FakeComposerClient::clearFrames() { - Mutex::Autolock _l(mStateMutex); - mFrames.clear(); - for (const std::unique_ptr<LayerImpl>& layer : mLayers) { - if (layer->mValid) { - layer->mRenderState.mSwapCount = 0; - } - } -} - -void FakeComposerClient::onSurfaceFlingerStart() { - mSurfaceComposer = nullptr; - do { - mSurfaceComposer = new android::SurfaceComposerClient; - android::status_t initResult = mSurfaceComposer->initCheck(); - if (initResult != android::NO_ERROR) { - ALOGD("Init result: %d", initResult); - mSurfaceComposer = nullptr; - std::this_thread::sleep_for(10ms); - } - } while (mSurfaceComposer == nullptr); - ALOGD("SurfaceComposerClient created"); - mSurfaceComposer->enableVSyncInjections(true); -} - -void FakeComposerClient::onSurfaceFlingerStop() { - mSurfaceComposer->enableVSyncInjections(false); - mSurfaceComposer->dispose(); - mSurfaceComposer.clear(); -} - -// Includes destroyed layers, stored in order of creation. -int FakeComposerClient::getLayerCount() const { - return mLayers.size(); -} - -Layer FakeComposerClient::getLayer(size_t index) const { - // NOTE: If/when passing calls through to actual implementation, - // this might get more involving. - return static_cast<Layer>(index); -} - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h deleted file mode 100644 index 600e765e9b..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 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. - */ - -#pragma once - -#include <chrono> - -#include <composer-hal/2.1/ComposerClient.h> -#include <composer-hal/2.2/ComposerClient.h> -#include <composer-hal/2.3/ComposerClient.h> -#include <composer-hal/2.4/ComposerClient.h> -#include <utils/Condition.h> - -#include "MockComposerHal.h" -#include "RenderState.h" - -using namespace android::hardware::graphics::common; -using namespace android::hardware::graphics::composer; -using namespace android::hardware::graphics::composer::V2_4; -using namespace android::hardware::graphics::composer::V2_4::hal; -using namespace android::hardware; -using namespace std::chrono_literals; - -namespace { -class LayerImpl; -class Frame; -class DelayedEventGenerator; -} // namespace - -namespace android { -class SurfaceComposerClient; -} // namespace android - -namespace sftest { -// NOTE: The ID's need to be exactly these. VR composer and parts of -// the SurfaceFlinger assume the display IDs to have these values -// despite the enum being documented as a display type. -// TODO: Reference to actual documentation -constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY); -constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL); - -class FakeComposerClient : public ComposerHal { -public: - FakeComposerClient(); - virtual ~FakeComposerClient(); - - void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; } - - bool hasCapability(hwc2_capability_t capability) override; - - std::string dumpDebugInfo() override; - void registerEventCallback(EventCallback* callback) override; - void unregisterEventCallback() override; - - uint32_t getMaxVirtualDisplayCount() override; - V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format, - Display* outDisplay) override; - V2_1::Error destroyVirtualDisplay(Display display) override; - V2_1::Error createLayer(Display display, Layer* outLayer) override; - V2_1::Error destroyLayer(Display display, Layer layer) override; - - V2_1::Error getActiveConfig(Display display, Config* outConfig) override; - V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height, - V1_0::PixelFormat format, - V1_0::Dataspace dataspace) override; - V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override; - V2_1::Error getDisplayAttribute(Display display, Config config, - V2_1::IComposerClient::Attribute attribute, - int32_t* outValue) override; - V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; - V2_1::Error getDisplayName(Display display, hidl_string* outName) override; - V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override; - V2_1::Error getDozeSupport(Display display, bool* outSupport) override; - V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes, - float* outMaxLuminance, float* outMaxAverageLuminance, - float* outMinLuminance) override; - - V2_1::Error setActiveConfig(Display display, Config config) override; - V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override; - V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override; - V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; - - V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override; - V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence, - int32_t dataspace, const std::vector<hwc_rect_t>& damage) override; - V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer, - int32_t releaseFence) override; - V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, - std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) override; - V2_1::Error acceptDisplayChanges(Display display) override; - V2_1::Error presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) override; - - V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override; - V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, - int32_t acquireFence) override; - V2_1::Error setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) override; - V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; - V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override; - V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override; - V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override; - V2_1::Error setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) override; - V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; - V2_1::Error setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) override; - V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override; - V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override; - V2_1::Error setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) override; - V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; - - // Composer 2.2 - V2_1::Error getPerFrameMetadataKeys( - Display display, - std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override; - V2_1::Error setLayerPerFrameMetadata( - Display display, Layer layer, - const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override; - - V2_1::Error getReadbackBufferAttributes( - Display display, graphics::common::V1_1::PixelFormat* outFormat, - graphics::common::V1_1::Dataspace* outDataspace) override; - V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle, - android::base::unique_fd fenceFd) override; - V2_1::Error getReadbackBufferFence(Display display, - android::base::unique_fd* outFenceFd) override; - V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height, - graphics::common::V1_1::PixelFormat* format, - Display* outDisplay) override; - V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height, - graphics::common::V1_1::PixelFormat format, - graphics::common::V1_1::Dataspace dataspace) override; - V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override; - - V2_1::Error setLayerFloatColor(Display display, Layer layer, - V2_2::IComposerClient::FloatColor color) override; - - V2_1::Error getColorModes_2_2(Display display, - hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override; - V2_1::Error getRenderIntents( - Display display, graphics::common::V1_1::ColorMode mode, - std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override; - V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode, - graphics::common::V1_1::RenderIntent intent) override; - - std::array<float, 16> getDataspaceSaturationMatrix( - graphics::common::V1_1::Dataspace dataspace) override; - - // Composer 2.3 - V2_1::Error getPerFrameMetadataKeys_2_3( - Display display, - std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override; - - V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode, - graphics::common::V1_1::RenderIntent intent) override; - - V2_1::Error getRenderIntents_2_3( - Display display, graphics::common::V1_2::ColorMode mode, - std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override; - - V2_1::Error getColorModes_2_3(Display display, - hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override; - - V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height, - graphics::common::V1_2::PixelFormat format, - graphics::common::V1_2::Dataspace dataspace) override; - V2_1::Error getReadbackBufferAttributes_2_3( - Display display, graphics::common::V1_2::PixelFormat* outFormat, - graphics::common::V1_2::Dataspace* outDataspace) override; - V2_1::Error getHdrCapabilities_2_3(Display display, - hidl_vec<graphics::common::V1_2::Hdr>* outTypes, - float* outMaxLuminance, float* outMaxAverageLuminance, - float* outMinLuminance) override; - V2_1::Error setLayerPerFrameMetadata_2_3( - Display display, Layer layer, - const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override; - V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort, - std::vector<uint8_t>* outData) override; - V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override; - V2_1::Error getDisplayedContentSamplingAttributes( - uint64_t display, graphics::common::V1_2::PixelFormat& format, - graphics::common::V1_2::Dataspace& dataspace, - hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override; - V2_1::Error setDisplayedContentSamplingEnabled( - uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable, - hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask, - uint64_t maxFrames) override; - V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp, - uint64_t& frameCount, - hidl_vec<uint64_t>& sampleComponent0, - hidl_vec<uint64_t>& sampleComponent1, - hidl_vec<uint64_t>& sampleComponent2, - hidl_vec<uint64_t>& sampleComponent3) override; - V2_1::Error getDisplayCapabilities( - Display display, - std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override; - V2_1::Error setLayerPerFrameMetadataBlobs( - Display display, Layer layer, - std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override; - V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; - V2_1::Error setDisplayBrightness(Display display, float brightness) override; - - // Composer 2.4 - void registerEventCallback_2_4(EventCallback_2_4* callback) override; - - void unregisterEventCallback_2_4() override; - - V2_4::Error getDisplayCapabilities_2_4( - Display display, - std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override; - V2_4::Error getDisplayConnectionType( - Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override; - V2_4::Error getDisplayAttribute_2_4(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) override; - V2_4::Error getDisplayVsyncPeriod(Display display, - V2_4::VsyncPeriodNanos* outVsyncPeriod) override; - V2_4::Error setActiveConfigWithConstraints( - Display display, Config config, - const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, - VsyncPeriodChangeTimeline* outTimeline) override; - V2_4::Error setAutoLowLatencyMode(Display display, bool on) override; - V2_4::Error getSupportedContentTypes( - Display display, - std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override; - V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override; - V2_4::Error validateDisplay_2_4( - Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks, - IComposerClient::ClientTargetProperty* outClientTargetProperty) override; - V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key, - bool mandatory, const std::vector<uint8_t>& value) override; - V2_4::Error getLayerGenericMetadataKeys( - std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override; - - void setClient(ComposerClient* client); - - void requestVSync(uint64_t vsyncTime = 0); - // We don't want tests hanging, so always use a timeout. Remember - // to always check the number of frames with test ASSERT_! - // Wait until next frame is rendered after requesting vsync. - void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms); - void runVSyncAfter(std::chrono::nanoseconds wait); - - int getFrameCount() const; - // We don't want tests hanging, so always use a timeout. Remember - // to always check the number of frames with test ASSERT_! - void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const; - std::vector<RenderState> getFrameRects(int frame) const; - std::vector<RenderState> getLatestFrame() const; - void clearFrames(); - - void onSurfaceFlingerStart(); - void onSurfaceFlingerStop(); - - int getLayerCount() const; - Layer getLayer(size_t index) const; - - void hotplugDisplay(Display display, IComposerCallback::Connection state); - void refreshDisplay(Display display); - -private: - LayerImpl& getLayerImpl(Layer handle); - - EventCallback* mEventCallback; - EventCallback_2_4* mEventCallback_2_4; - Config mCurrentConfig; - bool mVsyncEnabled; - std::vector<std::unique_ptr<LayerImpl>> mLayers; - std::vector<std::unique_ptr<Frame>> mFrames; - // Using a pointer to hide the implementation into the CPP file. - std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator; - android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections - mutable android::Mutex mStateMutex; - mutable android::Condition mFramesAvailable; - - MockComposerHal* mMockHal = nullptr; -}; - -} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp deleted file mode 100644 index c656eed0fb..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp +++ /dev/null @@ -1,179 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "FakeHwcService" -#include <log/log.h> - -#include "FakeComposerService.h" - -using namespace android::hardware; -using namespace android::hardware::graphics::composer; - -namespace sftest { - -FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client) - : mClient(client) {} - -FakeComposerService_2_1::~FakeComposerService_2_1() { - ALOGI("Maybe killing client %p", mClient.get()); - // Rely on sp to kill the client. -} - -Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) { - ALOGI("FakeComposerService::getCapabilities"); - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - ALOGI("FakeComposerService::dumpDebugInfo"); - hidl_cb(hidl_string()); - return Void(); -} - -Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) { - ALOGI("FakeComposerService::createClient %p", mClient.get()); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::NONE, mClient); - return Void(); -} - -FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client) - : mClient(client) {} - -FakeComposerService_2_2::~FakeComposerService_2_2() { - ALOGI("Maybe killing client %p", mClient.get()); - // Rely on sp to kill the client. -} - -Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) { - ALOGI("FakeComposerService::getCapabilities"); - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - ALOGI("FakeComposerService::dumpDebugInfo"); - hidl_cb(hidl_string()); - return Void(); -} - -Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) { - ALOGI("FakeComposerService::createClient %p", mClient.get()); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::NONE, mClient); - return Void(); -} - -FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client) - : mClient(client) {} - -FakeComposerService_2_3::~FakeComposerService_2_3() { - ALOGI("Maybe killing client %p", mClient.get()); - // Rely on sp to kill the client. -} - -Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) { - ALOGI("FakeComposerService::getCapabilities"); - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - ALOGI("FakeComposerService::dumpDebugInfo"); - hidl_cb(hidl_string()); - return Void(); -} - -Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) { - LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3"); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::UNSUPPORTED, nullptr); - return Void(); -} - -Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) { - ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get()); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::NONE, mClient); - return Void(); -} - -FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client) - : mClient(client) {} - -FakeComposerService_2_4::~FakeComposerService_2_4() { - ALOGI("Maybe killing client %p", mClient.get()); - // Rely on sp to kill the client. -} - -Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) { - ALOGI("FakeComposerService::getCapabilities"); - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - ALOGI("FakeComposerService::dumpDebugInfo"); - hidl_cb(hidl_string()); - return Void(); -} - -Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) { - LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4"); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::UNSUPPORTED, nullptr); - return Void(); -} - -Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) { - LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4"); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_1::Error::UNSUPPORTED, nullptr); - return Void(); -} - -Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) { - ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get()); - if (!mClient->init()) { - LOG_ALWAYS_FATAL("failed to initialize ComposerClient"); - } - hidl_cb(V2_4::Error::NONE, mClient); - return Void(); -} - -} // namespace sftest - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h deleted file mode 100644 index 47f970f5b2..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h +++ /dev/null @@ -1,92 +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. - */ - -#pragma once - -#include <android/hardware/graphics/composer/2.4/IComposer.h> -#include <composer-hal/2.1/ComposerClient.h> -#include <composer-hal/2.2/ComposerClient.h> -#include <composer-hal/2.3/ComposerClient.h> -#include <composer-hal/2.4/ComposerClient.h> - -using android::hardware::Return; - -using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient; - -namespace sftest { - -using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer; - -class FakeComposerService_2_1 : public IComposer_2_1 { -public: - explicit FakeComposerService_2_1(android::sp<ComposerClient>& client); - virtual ~FakeComposerService_2_1(); - - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - -private: - android::sp<ComposerClient> mClient; -}; - -using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer; -class FakeComposerService_2_2 : public IComposer_2_2 { -public: - explicit FakeComposerService_2_2(android::sp<ComposerClient>& client); - virtual ~FakeComposerService_2_2(); - - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - -private: - android::sp<ComposerClient> mClient; -}; - -using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer; -class FakeComposerService_2_3 : public IComposer_2_3 { -public: - explicit FakeComposerService_2_3(android::sp<ComposerClient>& client); - virtual ~FakeComposerService_2_3(); - - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override; - -private: - android::sp<ComposerClient> mClient; -}; - -using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer; - -class FakeComposerService_2_4 : public IComposer_2_4 { -public: - explicit FakeComposerService_2_4(android::sp<ComposerClient>& client); - virtual ~FakeComposerService_2_4(); - - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override; - Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override; - -private: - android::sp<ComposerClient> mClient; -}; - -} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp deleted file mode 100644 index 1cea25a80f..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "FakeHwcUtil" -#include <log/log.h> - -#include "FakeComposerUtils.h" -#include "RenderState.h" - -#include "SurfaceFlinger.h" // Get the name of the service... - -#include <binder/IServiceManager.h> -#include <cutils/properties.h> -#include <hidl/ServiceManagement.h> - -#include <iomanip> -#include <thread> - -using android::String16; -using android::sp; -using namespace std::chrono_literals; -using namespace sftest; -using std::setw; - -namespace sftest { - -// clang-format off -inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) { - os << std::fixed << std::setprecision(1) << "(" - << setw(align) << sourceRect.left << setw(0) << "," - << setw(align) << sourceRect.top << setw(0) << "," - << setw(align) << sourceRect.right << setw(0) << "," - << setw(align) << sourceRect.bottom << setw(0) << ")"; -} - -inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) { - os << "(" - << setw(align) << displayRect.left << setw(0) << "," - << setw(align) << displayRect.top << setw(0) << "," - << setw(align) << displayRect.right << setw(0) << "," - << setw(align) << displayRect.bottom << setw(0) << ")"; -} -// clang-format on - -inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) { - printSourceRectAligned(os, state.mSourceCrop, 7); - os << "->"; - printDisplayRectAligned(os, state.mDisplayFrame, 5); - return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3) - << state.mPlaneAlpha << " Xform:" << state.mTransform; -} - -// Helper for verifying the parts of the RenderState -template <typename T> -bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val, - const char* name) { - if (ref != val) { - message = message << "Expected " << name << ":" << ref << ", got:" << val << "."; - return false; - } - return true; -} - -::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) { - // TODO: Message could start as success and be assigned as failure. - // Only problem is that utility assumes it to be failure and just adds stuff. Would - // need still special case the initial failure in the utility? - // TODO: ... or would it be possible to break this back to gtest primitives? - ::testing::AssertionResult message = ::testing::AssertionFailure(); - bool passes = true; - - // The work here is mostly about providing good log strings for differences - passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame"); - passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha"); - passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count"); - passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop"); - // ... add more - if (passes) { - return ::testing::AssertionSuccess(); - } - return message; -} - -::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, - const std::vector<RenderState>& val) { - ::testing::AssertionResult message = ::testing::AssertionFailure(); - bool passed = true; - if (ref.size() != val.size()) { - message << "Expected " << ref.size() << " rects, got " << val.size() << "."; - passed = false; - } - for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) { - ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]); - if (rectResult == false) { - message << "First different rect at " << rectIndex << ": " << rectResult.message(); - passed = false; - break; - } - } - - if (passed) { - return ::testing::AssertionSuccess(); - } else { - message << "\nReference:"; - for (auto state = ref.begin(); state != ref.end(); ++state) { - message << "\n" << *state; - } - message << "\nActual:"; - for (auto state = val.begin(); state != val.end(); ++state) { - message << "\n" << *state; - } - } - return message; -} - -void startSurfaceFlinger() { - ALOGI("Start SurfaceFlinger"); - system("start surfaceflinger"); - - sp<android::IServiceManager> sm(android::defaultServiceManager()); - sp<android::IBinder> sf; - while (sf == nullptr) { - std::this_thread::sleep_for(10ms); - sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); - } - ALOGV("SurfaceFlinger running"); -} - -void stopSurfaceFlinger() { - ALOGI("Stop SurfaceFlinger"); - system("stop surfaceflinger"); - sp<android::IServiceManager> sm(android::defaultServiceManager()); - sp<android::IBinder> sf; - while (sf != nullptr) { - std::this_thread::sleep_for(10ms); - sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); - } - ALOGV("SurfaceFlinger stopped"); -} - -//////////////////////////////////////////////// - -void FakeHwcEnvironment::SetUp() { - ALOGI("Test env setup"); - system("setenforce 0"); - system("stop"); - property_set("debug.sf.nobootanimation", "1"); - { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.sf.nobootanimation", value, "0"); - LOG_FATAL_IF(atoi(value) != 1, "boot skip not set"); - } - // TODO: Try registering the mock as the default service instead. - property_set("debug.sf.hwc_service_name", "mock"); - - // This allows tests/SF to register/load a HIDL service not listed in manifest files. - android::hardware::details::setTrebleTestingOverride(true); - property_set("debug.sf.treble_testing_override", "true"); -} - -void FakeHwcEnvironment::TearDown() { - ALOGI("Test env tear down"); - system("stop"); - // Wait for mock call signaling teardown? - property_set("debug.sf.nobootanimation", "0"); - property_set("debug.sf.hwc_service_name", "default"); - system("setenforce 1"); - ALOGI("Test env tear down - done"); -} - -} // namespace sftest - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h deleted file mode 100644 index 383a111859..0000000000 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 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. - */ - -#pragma once - -#include "FakeComposerClient.h" - -#include <gui/SurfaceComposerClient.h> -#include <log/log.h> -#include <gtest/gtest.h> - -// clang-format off -// Note: This needs to reside in the global namespace for the GTest to use it -inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) { - return os << "(" << rect.left << "," - << rect.top << "," - << rect.right << "," - << rect.bottom << ")"; -} - -inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) { - return os << "(" << rect.left << "," - << rect.top << "," - << rect.right << "," - << rect.bottom << ")"; -} -// clang-format on - -namespace sftest { - -class RenderState; - -// clang-format off -inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) { - return a.top == b.top && - a.left == b.left && - a.bottom == b.bottom && - a.right == b.right; -} - -inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) { - return a.top == b.top && - a.left == b.left && - a.bottom == b.bottom && - a.right == b.right; -} -// clang-format on - -inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) { - return !(a == b); -} - -inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) { - return !(a == b); -} - -::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val); -::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, - const std::vector<RenderState>& val); - -void startSurfaceFlinger(); -void stopSurfaceFlinger(); - -class FakeHwcEnvironment : public ::testing::Environment { -public: - virtual ~FakeHwcEnvironment() {} - void SetUp() override; - void TearDown() override; -}; - -/* - * All surface state changes are supposed to happen inside a global - * transaction. TransactionScope object at the beginning of - * scope automates the process. The resulting scope gives a visual cue - * on the span of the transaction as well. - * - * Closing the transaction is synchronous, i.e., it waits for - * SurfaceFlinger to composite one frame. Now, the FakeComposerClient - * is built to explicitly request vsyncs one at the time. A delayed - * request must be made before closing the transaction or the test - * thread stalls until SurfaceFlinger does an emergency vsync by - * itself. TransactionScope encapsulates this vsync magic. - */ -class TransactionScope : public android::SurfaceComposerClient::Transaction { -public: - explicit TransactionScope(FakeComposerClient& composer) : Transaction(), mComposer(composer) {} - - ~TransactionScope() { - int frameCount = mComposer.getFrameCount(); - mComposer.runVSyncAfter(1ms); - LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply()); - // Make sure that exactly one frame has been rendered. - mComposer.waitUntilFrame(frameCount + 1); - // LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(), - // "Unexpected frame advance. Delta: %d", - // mComposer.getFrameCount() - frameCount); - } - - FakeComposerClient& mComposer; -}; - -} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h deleted file mode 100644 index 5dc3778153..0000000000 --- a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <composer-hal/2.4/ComposerClient.h> - -#include <gmock/gmock.h> - -using namespace android::hardware::graphics::common; -using namespace android::hardware::graphics::composer; -using namespace android::hardware::graphics::composer::V2_4; -using namespace android::hardware::graphics::composer::V2_4::hal; -using namespace android::hardware; -using namespace std::chrono_literals; - -namespace sftest { - -// Mock class for ComposerHal. Implements only the functions used in the test. -class MockComposerHal { -public: - MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*)); - MOCK_METHOD4(getDisplayAttribute_2_4, - V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*)); - MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*)); - MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config)); - MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*)); - MOCK_METHOD4(setActiveConfigWithConstraints, - V2_4::Error(Display, Config, - const V2_4::IComposerClient::VsyncPeriodChangeConstraints&, - VsyncPeriodChangeTimeline*)); -}; - -} // namespace sftest
\ No newline at end of file diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h deleted file mode 100644 index 40193f237e..0000000000 --- a/services/surfaceflinger/tests/fakehwc/RenderState.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 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. - */ - -#pragma once - -#include <vector> - -namespace sftest { -// Description of a rendered rectangle. Should only contain -// instructions necessary to rasterize the rectangle. The full scene -// is given as a sorted list of rectangles, bottom layer at index 0. -class RenderState { -public: - RenderState() = default; - // Default copy-ctor - - hwc_rect_t mDisplayFrame = {0, 0, 0, 0}; - hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f}; - std::vector<hwc_rect_t> mVisibleRegion; - hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE; - buffer_handle_t mBuffer = 0; - uint32_t mSwapCount = 0; // How many set buffer calls to the layer. - int32_t mAcquireFence = 0; // Probably should not be here. - float mPlaneAlpha = 0.f; - hwc_color_t mLayerColor = {0, 0, 0, 0}; - hwc_transform_t mTransform = static_cast<hwc_transform_t>(0); -}; - -} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp deleted file mode 100644 index b3b4ec15cd..0000000000 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ /dev/null @@ -1,1790 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - -// #define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "FakeHwcTest" - -#include "FakeComposerClient.h" -#include "FakeComposerService.h" -#include "FakeComposerUtils.h" -#include "MockComposerHal.h" - -#include <binder/Parcel.h> -#include <gui/DisplayEventReceiver.h> -#include <gui/ISurfaceComposer.h> -#include <gui/LayerDebugInfo.h> -#include <gui/LayerState.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> - -#include <android/hidl/manager/1.0/IServiceManager.h> -#include <android/looper.h> -#include <android/native_window.h> -#include <binder/ProcessState.h> -#include <hwbinder/ProcessState.h> -#include <log/log.h> -#include <private/gui/ComposerService.h> -#include <ui/DisplayMode.h> -#include <ui/DynamicDisplayInfo.h> -#include <utils/Looper.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <limits> -#include <thread> - -using namespace std::chrono_literals; - -using namespace android; -using namespace android::hardware; - -using namespace sftest; - -namespace { - -// Mock test helpers -using ::testing::_; -using ::testing::DoAll; -using ::testing::Return; -using ::testing::SetArgPointee; - -using Transaction = SurfaceComposerClient::Transaction; -using Attribute = V2_4::IComposerClient::Attribute; -using Display = V2_1::Display; - -/////////////////////////////////////////////// -constexpr PhysicalDisplayId physicalIdFromHwcDisplayId(Display hwcId) { - return PhysicalDisplayId::fromPort(hwcId); -} -constexpr PhysicalDisplayId kPrimaryDisplayId = physicalIdFromHwcDisplayId(PRIMARY_DISPLAY); -constexpr PhysicalDisplayId kExternalDisplayId = physicalIdFromHwcDisplayId(EXTERNAL_DISPLAY); - -struct TestColor { -public: - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; - -constexpr static TestColor RED = {195, 63, 63, 255}; -constexpr static TestColor LIGHT_RED = {255, 177, 177, 255}; -constexpr static TestColor GREEN = {63, 195, 63, 255}; -constexpr static TestColor BLUE = {63, 63, 195, 255}; -constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255}; - -// Fill an RGBA_8888 formatted surface with a single color. -static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color, - bool unlock = true) { - ANativeWindow_Buffer outBuffer; - sp<Surface> s = sc->getSurface(); - ASSERT_TRUE(s != nullptr); - ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); - uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; y++) { - for (int x = 0; x < outBuffer.width; x++) { - uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); - pixel[0] = color.r; - pixel[1] = color.g; - pixel[2] = color.b; - pixel[3] = color.a; - } - } - if (unlock) { - ASSERT_EQ(NO_ERROR, s->unlockAndPost()); - } -} - -inline RenderState makeSimpleRect(int left, int top, int right, int bottom) { - RenderState res; - res.mDisplayFrame = hwc_rect_t{left, top, right, bottom}; - res.mPlaneAlpha = 1.0f; - res.mSwapCount = 0; - res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left), - static_cast<float>(bottom - top)}; - return res; -} - -inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right, - unsigned int bottom) { - EXPECT_LE(left, static_cast<unsigned int>(INT_MAX)); - EXPECT_LE(top, static_cast<unsigned int>(INT_MAX)); - EXPECT_LE(right, static_cast<unsigned int>(INT_MAX)); - EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX)); - return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right), - static_cast<int>(bottom)); -} - -/////////////////////////////////////////////// -template <typename FakeComposerService> -class DisplayTest : public ::testing::Test { -protected: - struct TestConfig { - int32_t id; - int32_t w; - int32_t h; - int32_t vsyncPeriod; - int32_t group; - }; - - static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) { - auto self = static_cast<DisplayTest*>(data); - - ssize_t n; - DisplayEventReceiver::Event buffer[1]; - - while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) { - for (int i = 0; i < n; i++) { - self->mReceivedDisplayEvents.push_back(buffer[i]); - } - } - ALOGD_IF(n < 0, "Error reading events (%s)", strerror(-n)); - return 1; - } - - Error getDisplayAttributeNoMock(Display display, Config config, - V2_4::IComposerClient::Attribute attribute, int32_t* outValue) { - mFakeComposerClient->setMockHal(nullptr); - auto ret = - mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue); - mFakeComposerClient->setMockHal(mMockComposer.get()); - return ret; - } - - void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs, - Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) { - std::vector<Config> configIds; - for (size_t i = 0; i < testConfigs.size(); i++) { - configIds.push_back(testConfigs[i].id); - - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _)) - .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE))); - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _)) - .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE))); - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD, - _)) - .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod), - Return(Error::NONE))); - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP, - _)) - .WillRepeatedly( - DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE))); - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _)) - .WillRepeatedly(Return(Error::UNSUPPORTED)); - EXPECT_CALL(*mMockComposer, - getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _)) - .WillRepeatedly(Return(Error::UNSUPPORTED)); - } - - EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)), - Return(V2_1::Error::NONE))); - - EXPECT_CALL(*mMockComposer, getActiveConfig(display, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE))); - - EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _)) - .WillRepeatedly( - DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE))); - } - - void SetUp() override { - mMockComposer = std::make_unique<MockComposerHal>(); - mFakeComposerClient = new FakeComposerClient(); - mFakeComposerClient->setMockHal(mMockComposer.get()); - - sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(mFakeComposerClient); - mFakeService = new FakeComposerService(client); - ASSERT_EQ(android::OK, mFakeService->registerAsService("mock")); - - android::hardware::ProcessState::self()->startThreadPool(); - android::ProcessState::self()->startThreadPool(); - - setExpectationsForConfigs(PRIMARY_DISPLAY, - {{ - .id = 1, - .w = 1920, - .h = 1024, - .vsyncPeriod = 16'666'666, - .group = 0, - }}, - 1, 16'666'666); - - startSurfaceFlinger(); - - // Fake composer wants to enable VSync injection - mFakeComposerClient->onSurfaceFlingerStart(); - - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::EventRegistration::modeChanged)); - mLooper = new Looper(false); - mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this); - } - - void TearDown() override { - mLooper = nullptr; - mReceiver = nullptr; - - mComposerClient->dispose(); - mComposerClient = nullptr; - - // Fake composer needs to release SurfaceComposerClient before the stop. - mFakeComposerClient->onSurfaceFlingerStop(); - stopSurfaceFlinger(); - - mFakeComposerClient->setMockHal(nullptr); - - mFakeService = nullptr; - // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime - // management. - mMockComposer = nullptr; - } - - void waitForDisplayTransaction(Display display) { - // Both a refresh and a vsync event are needed to apply pending display - // transactions. - mFakeComposerClient->refreshDisplay(display); - mFakeComposerClient->runVSyncAndWait(); - - // Extra vsync and wait to avoid a 10% flake due to a race. - mFakeComposerClient->runVSyncAndWait(); - } - - bool waitForHotplugEvent(Display displayId, bool connected) { - return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected); - } - - bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) { - int waitCount = 20; - while (waitCount--) { - while (!mReceivedDisplayEvents.empty()) { - auto event = mReceivedDisplayEvents.front(); - mReceivedDisplayEvents.pop_front(); - - ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, - "event hotplug: displayId %s, connected %d", - to_string(event.header.displayId).c_str(), event.hotplug.connected); - - if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG && - event.header.displayId == displayId && event.hotplug.connected == connected) { - return true; - } - } - - mLooper->pollOnce(1); - } - return false; - } - - bool waitForModeChangedEvent(Display display, int32_t modeId) { - PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display); - int waitCount = 20; - while (waitCount--) { - while (!mReceivedDisplayEvents.empty()) { - auto event = mReceivedDisplayEvents.front(); - mReceivedDisplayEvents.pop_front(); - - ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, - "event mode: displayId %s, modeId %d", - to_string(event.header.displayId).c_str(), event.modeChange.modeId); - - if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE && - event.header.displayId == displayId && event.modeChange.modeId == modeId) { - return true; - } - } - - mLooper->pollOnce(1); - } - return false; - } - - void Test_HotplugOneConfig() { - ALOGD("DisplayTest::Test_Hotplug_oneConfig"); - - setExpectationsForConfigs(EXTERNAL_DISPLAY, - {{.id = 1, - .w = 200, - .h = 400, - .vsyncPeriod = 16'666'666, - .group = 0}}, - 1, 16'666'666); - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - - { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); - EXPECT_FALSE(display == nullptr); - - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - const ui::Size& resolution = mode.resolution; - EXPECT_EQ(ui::Size(200, 400), resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::DISCONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - mFakeComposerClient->clearFrames(); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false)); - - { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); - EXPECT_TRUE(display == nullptr); - - ui::DisplayMode mode; - EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - } - } - - void Test_HotplugTwoSeparateConfigs() { - ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs"); - - setExpectationsForConfigs(EXTERNAL_DISPLAY, - {{.id = 1, - .w = 200, - .h = 400, - .vsyncPeriod = 16'666'666, - .group = 0}, - {.id = 2, - .w = 800, - .h = 1600, - .vsyncPeriod = 11'111'111, - .group = 1}}, - 1, 16'666'666); - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); - EXPECT_FALSE(display == nullptr); - - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(200, 400), mode.resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - ui::DynamicDisplayInfo info; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); - const auto& modes = info.supportedDisplayModes; - EXPECT_EQ(modes.size(), 2); - - // change active mode - - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - for (int i = 0; i < modes.size(); i++) { - const auto& mode = modes[i]; - if (mode.resolution.getWidth() == 800) { - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate)); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i)); - break; - } - } - - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::DISCONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - mFakeComposerClient->clearFrames(); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false)); - } - - void Test_HotplugTwoConfigsSameGroup() { - ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup"); - - setExpectationsForConfigs(EXTERNAL_DISPLAY, - {{.id = 2, - .w = 800, - .h = 1600, - .vsyncPeriod = 16'666'666, - .group = 31}, - {.id = 3, - .w = 800, - .h = 1600, - .vsyncPeriod = 11'111'111, - .group = 31}}, - 2, 16'666'666); - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); - EXPECT_FALSE(display == nullptr); - - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - ui::DynamicDisplayInfo info; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); - const auto& modes = info.supportedDisplayModes; - EXPECT_EQ(modes.size(), 2); - - // change active mode - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - for (int i = 0; i < modes.size(); i++) { - const auto& mode = modes[i]; - if (mode.refreshRate == 1e9f / 11'111'111) { - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate)); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i)); - break; - } - } - - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::DISCONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - mFakeComposerClient->clearFrames(); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false)); - } - - void Test_HotplugThreeConfigsMixedGroups() { - ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups"); - - setExpectationsForConfigs(EXTERNAL_DISPLAY, - {{.id = 2, - .w = 800, - .h = 1600, - .vsyncPeriod = 16'666'666, - .group = 0}, - {.id = 3, - .w = 800, - .h = 1600, - .vsyncPeriod = 11'111'111, - .group = 0}, - {.id = 4, - .w = 1600, - .h = 3200, - .vsyncPeriod = 8'333'333, - .group = 1}, - {.id = 5, - .w = 1600, - .h = 3200, - .vsyncPeriod = 11'111'111, - .group = 1}}, - 2, 16'666'666); - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); - EXPECT_FALSE(display == nullptr); - - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - ui::DynamicDisplayInfo info; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); - const auto& modes = info.supportedDisplayModes; - EXPECT_EQ(modes.size(), 4); - - // change active mode to 800x1600@90Hz - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - for (size_t i = 0; i < modes.size(); i++) { - const auto& mode = modes[i]; - if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) { - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate)); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i)); - break; - } - } - - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - // change active mode to 1600x3200@120Hz - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - for (int i = 0; i < modes.size(); i++) { - const auto& mode = modes[i]; - if (mode.refreshRate == 1e9f / 8'333'333) { - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate)); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i)); - break; - } - } - - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(1600, 3200), mode.resolution); - EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - // change active mode to 1600x3200@90Hz - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - for (int i = 0; i < modes.size(); i++) { - const auto& mode = modes[i]; - if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) { - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate, - mode.refreshRate)); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i)); - break; - } - } - - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(1600, 3200), mode.resolution); - EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - - mFakeComposerClient->clearFrames(); - { - const ui::Size& resolution = mode.resolution; - auto surfaceControl = - mComposerClient->createSurface(String8("Display Test Surface Foo"), - resolution.getWidth(), resolution.getHeight(), - PIXEL_FORMAT_RGBA_8888, 0); - EXPECT_TRUE(surfaceControl != nullptr); - EXPECT_TRUE(surfaceControl->isValid()); - fillSurfaceRGBA8(surfaceControl, BLUE); - - { - TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); - } - } - - mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY, - V2_1::IComposerCallback::Connection::DISCONNECTED); - waitForDisplayTransaction(EXTERNAL_DISPLAY); - mFakeComposerClient->clearFrames(); - EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false)); - } - - void Test_HotplugPrimaryDisplay() { - ALOGD("DisplayTest::HotplugPrimaryDisplay"); - - mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY, - V2_1::IComposerCallback::Connection::DISCONNECTED); - - waitForDisplayTransaction(PRIMARY_DISPLAY); - - EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false)); - { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); - EXPECT_TRUE(display == nullptr); - - ui::DisplayMode mode; - auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode); - EXPECT_NE(NO_ERROR, result); - } - - mFakeComposerClient->clearFrames(); - - setExpectationsForConfigs(PRIMARY_DISPLAY, - {{.id = 1, - .w = 400, - .h = 200, - .vsyncPeriod = 16'666'666, - .group = 0}}, - 1, 16'666'666); - - mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY, - V2_1::IComposerCallback::Connection::CONNECTED); - - waitForDisplayTransaction(PRIMARY_DISPLAY); - - EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true)); - - { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); - EXPECT_FALSE(display == nullptr); - - ui::DisplayMode mode; - auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode); - EXPECT_EQ(NO_ERROR, result); - ASSERT_EQ(ui::Size(400, 200), mode.resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - } - } - - void Test_SubsequentHotplugConnectUpdatesDisplay(Display hwcDisplayId) { - ALOGD("DisplayTest::Test_SubsequentHotplugConnectUpdatesDisplay"); - - // Send a hotplug connected event to set up the initial display modes. - // The primary display is already connected so this will update it. - // If we're running the test of an external display this will create it. - setExpectationsForConfigs(hwcDisplayId, - {{.id = 1, - .w = 800, - .h = 1600, - .vsyncPeriod = 11'111'111, - .group = 1}}, - /* activeConfig */ 1, 11'111'111); - - mFakeComposerClient->hotplugDisplay(hwcDisplayId, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(hwcDisplayId); - EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true)); - - const auto displayId = physicalIdFromHwcDisplayId(hwcDisplayId); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(displayId); - EXPECT_FALSE(display == nullptr); - - // Verify that the active mode and the supported moded are updated - { - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - - ui::DynamicDisplayInfo info; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); - const auto& modes = info.supportedDisplayModes; - EXPECT_EQ(modes.size(), 1); - } - - // Send another hotplug connected event - setExpectationsForConfigs(hwcDisplayId, - { - {.id = 1, - .w = 800, - .h = 1600, - .vsyncPeriod = 16'666'666, - .group = 1}, - {.id = 2, - .w = 800, - .h = 1600, - .vsyncPeriod = 11'111'111, - .group = 1}, - {.id = 3, - .w = 800, - .h = 1600, - .vsyncPeriod = 8'333'333, - .group = 1}, - }, - /* activeConfig */ 1, 16'666'666); - - mFakeComposerClient->hotplugDisplay(hwcDisplayId, - V2_1::IComposerCallback::Connection::CONNECTED); - waitForDisplayTransaction(hwcDisplayId); - EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true)); - - // Verify that the active mode and the supported moded are updated - { - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); - } - - ui::DynamicDisplayInfo info; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); - const auto& modes = info.supportedDisplayModes; - EXPECT_EQ(modes.size(), 3); - - EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution); - EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate); - - EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution); - EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate); - - EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution); - EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate); - - // Verify that we are able to switch to any of the modes - for (int i = modes.size() - 1; i >= 0; i--) { - const auto hwcId = i + 1; - // Set up HWC expectations for the mode change - if (mIs2_4Client) { - EXPECT_CALL(*mMockComposer, - setActiveConfigWithConstraints(hwcDisplayId, hwcId, _, _)) - .WillOnce(Return(V2_4::Error::NONE)); - } else { - EXPECT_CALL(*mMockComposer, setActiveConfig(hwcDisplayId, hwcId)) - .WillOnce(Return(V2_1::Error::NONE)); - } - - EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate)); - // We need to refresh twice - once to apply the pending mode change request, - // and once to process the change. - waitForDisplayTransaction(hwcDisplayId); - waitForDisplayTransaction(hwcDisplayId); - EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i)) - << "Failure while switching to mode " << i; - - ui::DisplayMode mode; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - EXPECT_EQ(ui::Size(800, 1600), mode.resolution); - EXPECT_EQ(modes[i].refreshRate, mode.refreshRate); - } - } - - sp<V2_1::IComposer> mFakeService; - sp<SurfaceComposerClient> mComposerClient; - - std::unique_ptr<MockComposerHal> mMockComposer; - FakeComposerClient* mFakeComposerClient; - - std::unique_ptr<DisplayEventReceiver> mReceiver; - sp<Looper> mLooper; - std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents; - - static constexpr bool mIs2_4Client = - std::is_same<FakeComposerService, FakeComposerService_2_4>::value; -}; - -using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>; - -// Tests that VSYNC injection can be safely toggled while invalidating. -TEST_F(DisplayTest_2_1, VsyncInjection) { - const auto flinger = ComposerService::getComposerService(); - bool enable = true; - - for (int i = 0; i < 100; i++) { - flinger->enableVSyncInjections(enable); - enable = !enable; - - constexpr uint32_t kForceInvalidate = 1004; - android::Parcel data, reply; - data.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); - EXPECT_EQ(NO_ERROR, - android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply)); - - std::this_thread::sleep_for(5ms); - } -} - -TEST_F(DisplayTest_2_1, HotplugOneConfig) { - Test_HotplugOneConfig(); -} - -TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) { - Test_HotplugTwoSeparateConfigs(); -} - -TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) { - Test_HotplugTwoConfigsSameGroup(); -} - -TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) { - Test_HotplugThreeConfigsMixedGroups(); -} - -TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) { - Test_HotplugPrimaryDisplay(); -} - -TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesPrimaryDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY); -} - -TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesExternalDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY); -} - -using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>; - -TEST_F(DisplayTest_2_2, HotplugOneConfig) { - Test_HotplugOneConfig(); -} - -TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) { - Test_HotplugTwoSeparateConfigs(); -} - -TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) { - Test_HotplugTwoConfigsSameGroup(); -} - -TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) { - Test_HotplugThreeConfigsMixedGroups(); -} - -TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) { - Test_HotplugPrimaryDisplay(); -} - -TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesPrimaryDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY); -} - -TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesExternalDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY); -} - -using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>; - -TEST_F(DisplayTest_2_3, HotplugOneConfig) { - Test_HotplugOneConfig(); -} - -TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) { - Test_HotplugTwoSeparateConfigs(); -} - -TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) { - Test_HotplugTwoConfigsSameGroup(); -} - -TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) { - Test_HotplugThreeConfigsMixedGroups(); -} - -TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) { - Test_HotplugPrimaryDisplay(); -} - -TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesPrimaryDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY); -} - -TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesExternalDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY); -} - -using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>; - -TEST_F(DisplayTest_2_4, HotplugOneConfig) { - Test_HotplugOneConfig(); -} - -TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) { - Test_HotplugTwoSeparateConfigs(); -} - -TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) { - Test_HotplugTwoConfigsSameGroup(); -} - -TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) { - Test_HotplugThreeConfigsMixedGroups(); -} - -TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) { - Test_HotplugPrimaryDisplay(); -} - -TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesPrimaryDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY); -} - -TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesExternalDisplay) { - Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY); -} - -//////////////////////////////////////////////// - -template <typename FakeComposerService> -class TransactionTest : public ::testing::Test { -protected: - // Layer array indexing constants. - constexpr static int BG_LAYER = 0; - constexpr static int FG_LAYER = 1; - - static void SetUpTestCase() { - // TODO: See TODO comment at DisplayTest::SetUp for background on - // the lifetime of the FakeComposerClient. - sFakeComposer = new FakeComposerClient; - sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(sFakeComposer); - sp<V2_1::IComposer> fakeService = new FakeComposerService(client); - (void)fakeService->registerAsService("mock"); - - android::hardware::ProcessState::self()->startThreadPool(); - android::ProcessState::self()->startThreadPool(); - - startSurfaceFlinger(); - - // Fake composer wants to enable VSync injection - sFakeComposer->onSurfaceFlingerStart(); - } - - static void TearDownTestCase() { - // Fake composer needs to release SurfaceComposerClient before the stop. - sFakeComposer->onSurfaceFlingerStop(); - stopSurfaceFlinger(); - // TODO: This is deleted when the ComposerClient calls - // removeClient. Devise better lifetime control. - sFakeComposer = nullptr; - } - - void SetUp() override { - ALOGI("TransactionTest::SetUp"); - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - ALOGI("TransactionTest::SetUp - display"); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); - ASSERT_FALSE(display == nullptr); - - ui::DisplayMode mode; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); - - const ui::Size& resolution = mode.resolution; - mDisplayWidth = resolution.getWidth(); - mDisplayHeight = resolution.getHeight(); - - // Background surface - mBGSurfaceControl = - mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth, - mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mBGSurfaceControl != nullptr); - ASSERT_TRUE(mBGSurfaceControl->isValid()); - fillSurfaceRGBA8(mBGSurfaceControl, BLUE); - - // Foreground surface - mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, - PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mFGSurfaceControl != nullptr); - ASSERT_TRUE(mFGSurfaceControl->isValid()); - - fillSurfaceRGBA8(mFGSurfaceControl, RED); - - Transaction t; - t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - - t.setLayer(mBGSurfaceControl, INT32_MAX - 2); - t.show(mBGSurfaceControl); - - t.setLayer(mFGSurfaceControl, INT32_MAX - 1); - t.setPosition(mFGSurfaceControl, 64, 64); - t.show(mFGSurfaceControl); - - // Synchronous transaction will stop this thread, so we set up a - // delayed, off-thread vsync request before closing the - // transaction. In the test code this is usually done with - // TransactionScope. Leaving here in the 'vanilla' form for - // reference. - ASSERT_EQ(0, sFakeComposer->getFrameCount()); - sFakeComposer->runVSyncAfter(1ms); - t.apply(); - sFakeComposer->waitUntilFrame(1); - - // Reference data. This is what the HWC should see. - static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing"); - mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight)); - mBaseFrame[BG_LAYER].mSwapCount = 1; - mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); - mBaseFrame[FG_LAYER].mSwapCount = 1; - - auto frame = sFakeComposer->getFrameRects(0); - ASSERT_TRUE(framesAreSame(mBaseFrame, frame)); - } - - void TearDown() override { - ALOGD("TransactionTest::TearDown"); - - mComposerClient->dispose(); - mBGSurfaceControl = 0; - mFGSurfaceControl = 0; - mComposerClient = 0; - - sFakeComposer->runVSyncAndWait(); - mBaseFrame.clear(); - sFakeComposer->clearFrames(); - ASSERT_EQ(0, sFakeComposer->getFrameCount()); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - std::vector<LayerDebugInfo> layers; - status_t result = sf->getLayerDebugInfo(&layers); - if (result != NO_ERROR) { - ALOGE("Failed to get layers %s %d", strerror(-result), result); - } else { - // If this fails, the test being torn down leaked layers. - EXPECT_EQ(0u, layers.size()); - if (layers.size() > 0) { - for (auto layer = layers.begin(); layer != layers.end(); ++layer) { - std::cout << to_string(*layer).c_str(); - } - // To ensure the next test has clean slate, will run the class - // tear down and setup here. - TearDownTestCase(); - SetUpTestCase(); - } - } - ALOGD("TransactionTest::TearDown - complete"); - } - - void Test_LayerMove() { - ALOGD("TransactionTest::LayerMove"); - - // The scope opens and closes a global transaction and, at the - // same time, makes sure the SurfaceFlinger progresses one frame - // after the transaction closes. The results of the transaction - // should be available in the latest frame stored by the fake - // composer. - { - TransactionScope ts(*sFakeComposer); - ts.setPosition(mFGSurfaceControl, 128, 128); - // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls. - // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?) - // - // sFakeComposer->runVSyncAndWait(); - } - - fillSurfaceRGBA8(mFGSurfaceControl, GREEN); - sFakeComposer->runVSyncAndWait(); - - ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and - // there's no extra frames. - - // NOTE: Frame 0 is produced in the SetUp. - auto frame1Ref = mBaseFrame; - frame1Ref[FG_LAYER].mDisplayFrame = - hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves. - EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); - - auto frame2Ref = frame1Ref; - frame2Ref[FG_LAYER].mSwapCount++; - EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); - } - - void Test_LayerCrop() { - // TODO: Add scaling to confirm that crop happens in buffer space? - { - TransactionScope ts(*sFakeComposer); - Rect cropRect(16, 16, 32, 32); - ts.setCrop(mFGSurfaceControl, cropRect); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f}; - referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32}; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerSetLayer() { - { - TransactionScope ts(*sFakeComposer); - ts.setLayer(mFGSurfaceControl, INT_MAX - 3); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // The layers will switch order, but both are rendered because the background layer is - // transparent (RGBA8888). - std::vector<RenderState> referenceFrame(2); - referenceFrame[0] = mBaseFrame[FG_LAYER]; - referenceFrame[1] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerSetLayerOpaque() { - { - TransactionScope ts(*sFakeComposer); - ts.setLayer(mFGSurfaceControl, INT_MAX - 3); - ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, - layer_state_t::eLayerOpaque); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // The former foreground layer is now covered with opaque layer - it should have disappeared - std::vector<RenderState> referenceFrame(1); - referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - } - - void Test_SetLayerStack() { - ALOGD("TransactionTest::SetLayerStack"); - { - TransactionScope ts(*sFakeComposer); - ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1}); - } - - // Foreground layer should have disappeared. - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - std::vector<RenderState> refFrame(1); - refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerShowHide() { - ALOGD("TransactionTest::LayerShowHide"); - { - TransactionScope ts(*sFakeComposer); - ts.hide(mFGSurfaceControl); - } - - // Foreground layer should have disappeared. - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - std::vector<RenderState> refFrame(1); - refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*sFakeComposer); - ts.show(mFGSurfaceControl); - } - - // Foreground layer should be back - ASSERT_EQ(3, sFakeComposer->getFrameCount()); - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerSetAlpha() { - { - TransactionScope ts(*sFakeComposer); - ts.setAlpha(mFGSurfaceControl, 0.75f); - } - - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerSetFlags() { - { - TransactionScope ts(*sFakeComposer); - ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden, - layer_state_t::eLayerHidden); - } - - // Foreground layer should have disappeared. - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - std::vector<RenderState> refFrame(1); - refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); - } - - void Test_LayerSetMatrix() { - struct matrixTestData { - float matrix[4]; - hwc_transform_t expectedTransform; - hwc_rect_t expectedDisplayFrame; - }; - - // The matrix operates on the display frame and is applied before - // the position is added. So, the foreground layer rect is (0, 0, - // 64, 64) is first transformed, potentially yielding negative - // coordinates and then the position (64, 64) is added yielding - // the final on-screen rectangles given. - - const matrixTestData MATRIX_TESTS[7] = // clang-format off - {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}}, - {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}}, - {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}}, - {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}}, - {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}}, - {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}}, - {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}}; - // clang-format on - constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData); - - for (int i = 0; i < TEST_COUNT; i++) { - // TODO: How to leverage the HWC2 stringifiers? - const matrixTestData& xform = MATRIX_TESTS[i]; - SCOPED_TRACE(i); - { - TransactionScope ts(*sFakeComposer); - ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2], - xform.matrix[3]); - } - - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mTransform = xform.expectedTransform; - referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame; - - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - } - } - - void Test_SetRelativeLayer() { - constexpr int RELATIVE_LAYER = 2; - auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, - 64, PIXEL_FORMAT_RGBA_8888, 0); - fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED); - - // Now we stack the surface above the foreground surface and make sure it is visible. - { - TransactionScope ts(*sFakeComposer); - ts.setPosition(relativeSurfaceControl, 64, 64); - ts.show(relativeSurfaceControl); - ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1); - } - auto referenceFrame = mBaseFrame; - // NOTE: All three layers will be visible as the surfaces are - // transparent because of the RGBA format. - referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); - referenceFrame[RELATIVE_LAYER].mSwapCount = 1; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); - - // A call to setLayer will override a call to setRelativeLayer - { - TransactionScope ts(*sFakeComposer); - ts.setLayer(relativeSurfaceControl, 0); - } - - // Previous top layer will now appear at the bottom. - auto referenceFrame2 = mBaseFrame; - referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]); - EXPECT_EQ(3, sFakeComposer->getFrameCount()); - EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); - } - - sp<SurfaceComposerClient> mComposerClient; - sp<SurfaceControl> mBGSurfaceControl; - sp<SurfaceControl> mFGSurfaceControl; - std::vector<RenderState> mBaseFrame; - uint32_t mDisplayWidth; - uint32_t mDisplayHeight; - - static inline FakeComposerClient* sFakeComposer; -}; - -using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>; - -TEST_F(TransactionTest_2_1, DISABLED_LayerMove) { - Test_LayerMove(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) { - Test_LayerCrop(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) { - Test_LayerSetLayer(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) { - Test_LayerSetLayerOpaque(); -} - -TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) { - Test_SetLayerStack(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) { - Test_LayerShowHide(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) { - Test_LayerSetAlpha(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) { - Test_LayerSetFlags(); -} - -TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) { - Test_LayerSetMatrix(); -} - -TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) { - Test_SetRelativeLayer(); -} - -template <typename FakeComposerService> -class ChildLayerTest : public TransactionTest<FakeComposerService> { - using Base = TransactionTest<FakeComposerService>; - -protected: - constexpr static int CHILD_LAYER = 2; - - void SetUp() override { - Base::SetUp(); - mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, - Base::mFGSurfaceControl->getHandle()); - fillSurfaceRGBA8(mChild, LIGHT_GRAY); - - Base::sFakeComposer->runVSyncAndWait(); - Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); - Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1; - ASSERT_EQ(2, Base::sFakeComposer->getFrameCount()); - ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame())); - } - - void TearDown() override { - mChild = 0; - Base::TearDown(); - } - - void Test_Positioning() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 10, 10); - // Move to the same position as in the original setup. - ts.setPosition(Base::mFGSurfaceControl, 64, 64); - } - - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; - referenceFrame[CHILD_LAYER].mDisplayFrame = - hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - } - - auto referenceFrame2 = Base::mBaseFrame; - referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64}; - referenceFrame2[CHILD_LAYER].mDisplayFrame = - hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10}; - EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame())); - } - - void Test_Cropping() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 0, 0); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5)); - } - // NOTE: The foreground surface would be occluded by the child - // now, but is included in the stack because the child is - // transparent. - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - } - - void Test_Constraints() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(mChild); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - ts.setPosition(mChild, 63, 63); - } - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64}; - referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f}; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - } - - void Test_Scaling() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - } - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0); - } - - auto referenceFrame2 = Base::mBaseFrame; - referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; - referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; - EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame())); - } - - void Test_LayerAlpha() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 0, 0); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - ts.setAlpha(mChild, 0.5); - } - - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; - referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setAlpha(Base::mFGSurfaceControl, 0.5); - } - - auto referenceFrame2 = referenceFrame; - referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f; - referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f; - EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame())); - } - - sp<SurfaceControl> mChild; -}; - -using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>; - -TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) { - Test_Positioning(); -} - -TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) { - Test_Cropping(); -} - -TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) { - Test_Constraints(); -} - -TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) { - Test_Scaling(); -} - -TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) { - Test_LayerAlpha(); -} - -template <typename FakeComposerService> -class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> { - using Base = ChildLayerTest<FakeComposerService>; - -protected: - void SetUp() override { - Base::SetUp(); - Base::mChild = - Base::mComposerClient->createSurface(String8("Child surface"), 0, 0, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect, - Base::mFGSurfaceControl->getHandle()); - { - TransactionScope ts(*Base::sFakeComposer); - ts.setColor(Base::mChild, - {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f}); - ts.setCrop(Base::mChild, Rect(0, 0, 10, 10)); - } - - Base::sFakeComposer->runVSyncAndWait(); - Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); - Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f}; - Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0; - ASSERT_EQ(2, Base::sFakeComposer->getFrameCount()); - ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame())); - } - - void Test_LayerAlpha() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(Base::mChild); - ts.setPosition(Base::mChild, 0, 0); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - ts.setAlpha(Base::mChild, 0.5); - } - - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; - referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; - referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setAlpha(Base::mFGSurfaceControl, 0.5); - } - - auto referenceFrame2 = referenceFrame; - referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f; - referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f; - EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame())); - } - - void Test_LayerZeroAlpha() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(Base::mChild); - ts.setPosition(Base::mChild, 0, 0); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - ts.setAlpha(Base::mChild, 0.5); - } - - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; - referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; - referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setAlpha(Base::mFGSurfaceControl, 0.0f); - } - - std::vector<RenderState> refFrame(1); - refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER]; - - EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame())); - } -}; - -using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>; - -TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) { - Test_LayerAlpha(); -} - -TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) { - Test_LayerZeroAlpha(); -} -} // namespace - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - - auto* fakeEnvironment = new sftest::FakeHwcEnvironment; - ::testing::AddGlobalTestEnvironment(fakeEnvironment); - ::testing::InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp index ac4354cde1..0e214af706 100644 --- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp +++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp @@ -83,6 +83,37 @@ protected: std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{}; +struct LayerInfo { + int id; + std::string name; + int parent; + int z; + uint64_t curr_frame; + float x; + float y; +}; + +bool operator==(const LayerInfo& lh, const LayerInfo& rh) { + return std::make_tuple(lh.id, lh.name, lh.parent, lh.z, lh.curr_frame) == + std::make_tuple(rh.id, rh.name, rh.parent, rh.z, rh.curr_frame); +} + +bool compareById(const LayerInfo& a, const LayerInfo& b) { + return a.id < b.id; +} + +inline void PrintTo(const LayerInfo& info, ::std::ostream* os) { + *os << "Layer [" << info.id << "] name=" << info.name << " parent=" << info.parent + << " z=" << info.z << " curr_frame=" << info.curr_frame << " x=" << info.x + << " y=" << info.y; +} + +struct find_id : std::unary_function<LayerInfo, bool> { + int id; + find_id(int id) : id(id) {} + bool operator()(LayerInfo const& m) const { return m.id == id; } +}; + TEST_P(TransactionTraceTestSuite, validateEndState) { ASSERT_GT(mActualLayersTraceProto.entry_size(), 0); ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0); @@ -92,19 +123,64 @@ TEST_P(TransactionTraceTestSuite, validateEndState) { auto actualLastEntry = mActualLayersTraceProto.entry(mActualLayersTraceProto.entry_size() - 1); EXPECT_EQ(expectedLastEntry.layers().layers_size(), actualLastEntry.layers().layers_size()); - for (int i = 0; - i < expectedLastEntry.layers().layers_size() && i < actualLastEntry.layers().layers_size(); - i++) { - auto expectedLayer = expectedLastEntry.layers().layers(i); - auto actualLayer = actualLastEntry.layers().layers(i); - EXPECT_EQ(expectedLayer.id(), actualLayer.id()); - EXPECT_EQ(expectedLayer.name(), actualLayer.name()); - EXPECT_EQ(expectedLayer.parent(), actualLayer.parent()); - EXPECT_EQ(expectedLayer.z(), actualLayer.z()); - EXPECT_EQ(expectedLayer.curr_frame(), actualLayer.curr_frame()); - ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayer.name().c_str(), - expectedLayer.id(), expectedLayer.parent(), expectedLayer.z(), - expectedLayer.curr_frame()); + + std::vector<LayerInfo> expectedLayers; + expectedLayers.reserve(static_cast<size_t>(expectedLastEntry.layers().layers_size())); + for (int i = 0; i < expectedLastEntry.layers().layers_size(); i++) { + auto layer = expectedLastEntry.layers().layers(i); + expectedLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(), + layer.curr_frame(), + layer.has_position() ? layer.position().x() : -1, + layer.has_position() ? layer.position().y() : -1}); + } + std::sort(expectedLayers.begin(), expectedLayers.end(), compareById); + + std::vector<LayerInfo> actualLayers; + actualLayers.reserve(static_cast<size_t>(actualLastEntry.layers().layers_size())); + for (int i = 0; i < actualLastEntry.layers().layers_size(); i++) { + auto layer = actualLastEntry.layers().layers(i); + actualLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(), + layer.curr_frame(), + layer.has_position() ? layer.position().x() : -1, + layer.has_position() ? layer.position().y() : -1}); + } + std::sort(actualLayers.begin(), actualLayers.end(), compareById); + + size_t i = 0; + for (; i < actualLayers.size() && i < expectedLayers.size(); i++) { + auto it = std::find_if(actualLayers.begin(), actualLayers.end(), + find_id(expectedLayers[i].id)); + EXPECT_NE(it, actualLayers.end()); + EXPECT_EQ(expectedLayers[i], *it); + ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayers[i].name.c_str(), + expectedLayers[i].id, expectedLayers[i].parent, expectedLayers[i].z, + expectedLayers[i].curr_frame); + } + + EXPECT_EQ(expectedLayers.size(), actualLayers.size()); + + if (i < actualLayers.size()) { + for (size_t j = 0; j < actualLayers.size(); j++) { + if (std::find_if(expectedLayers.begin(), expectedLayers.end(), + find_id(actualLayers[j].id)) == expectedLayers.end()) { + ALOGD("actualLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, actualLayers[j].id, + actualLayers[j].name.c_str(), actualLayers[j].parent, actualLayers[j].z, + actualLayers[j].curr_frame); + } + } + FAIL(); + } + + if (i < expectedLayers.size()) { + for (size_t j = 0; j < expectedLayers.size(); j++) { + if (std::find_if(actualLayers.begin(), actualLayers.end(), + find_id(expectedLayers[j].id)) == actualLayers.end()) { + ALOGD("expectedLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, expectedLayers[j].id, + expectedLayers[j].name.c_str(), expectedLayers[j].parent, expectedLayers[j].z, + expectedLayers[j].curr_frame); + } + } + FAIL(); } } diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope Binary files differnew file mode 100644 index 0000000000..16a91ee6ee --- /dev/null +++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope Binary files differnew file mode 100644 index 0000000000..cd62ab8ce0 --- /dev/null +++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp index 53de4a6e56..513f77989b 100644 --- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp +++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp @@ -50,20 +50,15 @@ protected: sp<NiceMock<MockIPower>> mMockHal = nullptr; sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr; void verifyAndClearExpectations(); - void sendActualWorkDurationGroup(std::vector<WorkDuration> durations, - std::chrono::nanoseconds sleepBeforeLastSend); - std::chrono::nanoseconds mAllowedDeviation; - std::chrono::nanoseconds mStaleTimeout; + void sendActualWorkDurationGroup(std::vector<WorkDuration> durations); + static constexpr std::chrono::duration kStaleTimeout = 100ms; }; void AidlPowerHalWrapperTest::SetUp() { - mMockHal = new NiceMock<MockIPower>(); - mMockSession = new NiceMock<MockIPowerHintSession>(); + mMockHal = sp<NiceMock<MockIPower>>::make(); + mMockSession = sp<NiceMock<MockIPowerHintSession>>::make(); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); - mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); - mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; - mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; } void AidlPowerHalWrapperTest::verifyAndClearExpectations() { @@ -71,14 +66,11 @@ void AidlPowerHalWrapperTest::verifyAndClearExpectations() { Mock::VerifyAndClearExpectations(mMockSession.get()); } -void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( - std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) { +void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(std::vector<WorkDuration> durations) { for (size_t i = 0; i < durations.size(); i++) { - if (i == durations.size() - 1) { - std::this_thread::sleep_for(sleepBeforeLastSend); - } auto duration = durations[i]; - mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); + mWrapper->sendActualWorkDuration(Duration::fromNs(duration.durationNanos), + TimePoint::fromNs(duration.timeStampNanos)); } } @@ -164,13 +156,13 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { for (const auto& test : testCases) { // reset to 100ms baseline - mWrapper->setTargetWorkDuration(1); - mWrapper->setTargetWorkDuration(base.count()); + mWrapper->setTargetWorkDuration(1ns); + mWrapper->setTargetWorkDuration(base); - auto target = test.first; + std::chrono::nanoseconds target = test.first; EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count())) .Times(test.second ? 1 : 0); - mWrapper->setTargetWorkDuration(target.count()); + mWrapper->setTargetWorkDuration(target); verifyAndClearExpectations(); } } @@ -187,7 +179,7 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) { EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); - mWrapper->setTargetWorkDuration(1); + mWrapper->setTargetWorkDuration(1ns); EXPECT_TRUE(mWrapper->shouldReconnectHAL()); } @@ -206,58 +198,24 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { // 100ms const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> testCases = {{{{-1ms, 100}}, false}, - {{{100ms - (mAllowedDeviation / 2), 100}}, false}, - {{{100ms + (mAllowedDeviation / 2), 100}}, false}, - {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, - {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, + {{{50ms, 100}}, true}, {{{100ms, 100}, {200ms, 200}}, true}, {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, mStaleTimeout); + sendActualWorkDurationGroup({base}); auto raw = test.first; std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), [](auto d) { return toWorkDuration(d); }); - EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) - .Times(test.second ? 1 : 0); - sendActualWorkDurationGroup(durations, 0ms); - verifyAndClearExpectations(); - } -} - -TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { - ASSERT_TRUE(mWrapper->supportsPowerHintSession()); - - std::vector<int32_t> threadIds = {1, 2}; - mWrapper->setPowerHintSessionThreadIds(threadIds); - EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); - ASSERT_TRUE(mWrapper->startPowerHintSession()); - verifyAndClearExpectations(); - - auto base = toWorkDuration(100ms, 0); - // test cases with actual work durations and whether it should update hint against baseline - // 100ms - const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, - std::chrono::nanoseconds, bool>> - testCases = {{{{100ms, 100}}, mStaleTimeout, true}, - {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, - {{{100ms, 100}}, mStaleTimeout / 2, false}}; - - for (const auto& test : testCases) { - // reset actual duration - sendActualWorkDurationGroup({base}, mStaleTimeout); - - auto raw = std::get<0>(test); - std::vector<WorkDuration> durations(raw.size()); - std::transform(raw.begin(), raw.end(), durations.begin(), - [](auto d) { return toWorkDuration(d); }); - EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) - .Times(std::get<2>(test) ? 1 : 0); - sendActualWorkDurationGroup(durations, std::get<1>(test)); + for (auto& duration : durations) { + EXPECT_CALL(*mMockSession.get(), + reportActualWorkDuration(std::vector<WorkDuration>{duration})) + .Times(test.second ? 1 : 0); + } + sendActualWorkDurationGroup(durations); verifyAndClearExpectations(); } } @@ -275,7 +233,7 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) { duration.durationNanos = 1; EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); - sendActualWorkDurationGroup({duration}, 0ms); + sendActualWorkDurationGroup({duration}); EXPECT_TRUE(mWrapper->shouldReconnectHAL()); } diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 339d746fac..bf22521dfe 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -34,7 +34,6 @@ filegroup { "mock/MockFrameTimeline.cpp", "mock/MockFrameTracer.cpp", "mock/MockNativeWindowSurface.cpp", - "mock/MockSurfaceInterceptor.cpp", "mock/MockTimeStats.cpp", "mock/MockVsyncController.cpp", "mock/MockVSyncTracker.cpp", @@ -71,7 +70,6 @@ cc_test { ":libsurfaceflinger_sources", "libsurfaceflinger_unittest_main.cpp", "AidlPowerHalWrapperTest.cpp", - "CachingTest.cpp", "CompositionTest.cpp", "DispSyncSourceTest.cpp", "DisplayIdGeneratorTest.cpp", @@ -86,6 +84,7 @@ cc_test { "FpsTest.cpp", "FramebufferSurfaceTest.cpp", "FrameRateOverrideMappingsTest.cpp", + "FrameRateSelectionPriorityTest.cpp", "FrameTimelineTest.cpp", "GameModeTest.cpp", "HWComposerTest.cpp", @@ -93,6 +92,8 @@ cc_test { "LayerHistoryTest.cpp", "LayerInfoTest.cpp", "LayerMetadataTest.cpp", + "LayerHierarchyTest.cpp", + "LayerLifecycleManagerTest.cpp", "LayerTest.cpp", "LayerTestUtils.cpp", "MessageQueueTest.cpp", @@ -109,10 +110,11 @@ cc_test { "SurfaceFlinger_SetDisplayStateTest.cpp", "SurfaceFlinger_SetPowerModeInternalTest.cpp", "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", + "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp", + "SurfaceFlinger_ExcludeDolbyVisionTest.cpp", "SchedulerTest.cpp", "SetFrameRateTest.cpp", - "RefreshRateConfigsTest.cpp", - "RefreshRateSelectionTest.cpp", + "RefreshRateSelectorTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", "TimeStatsTest.cpp", @@ -135,20 +137,22 @@ cc_test { cc_defaults { name: "libsurfaceflinger_mocks_defaults", + defaults: [ + "android.hardware.graphics.common-ndk_static", + "android.hardware.graphics.composer3-ndk_static", + ], static_libs: [ "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", - "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.graphics.composer3-V1-ndk", "android.hardware.power@1.0", "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libaidlcommonsupport", "libcompositionengine_mocks", "libcompositionengine", @@ -165,7 +169,6 @@ cc_defaults { "libtimestats_atoms_proto", "libtimestats_proto", "libtonemap", - "libtrace_proto", "perfetto_trace_protos", ], shared_libs: [ diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp deleted file mode 100644 index 6f85498670..0000000000 --- a/services/surfaceflinger/tests/unittests/CachingTest.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "CachingTest" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/BufferQueue.h> -#include "BufferStateLayer.h" - -namespace android { - -class SlotGenerationTest : public testing::Test { -protected: - sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator = - sp<BufferStateLayer::HwcSlotGenerator>::make(); - 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)}; - sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; -}; - -TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) { - sp<IBinder> binder = new BBinder(); - // test getting invalid client_cache_id - client_cache_t id; - int slot = mHwcSlotGenerator->getHwcCacheSlot(id); - EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot); -} - -TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) { - sp<IBinder> binder = new BBinder(); - client_cache_t id; - id.token = binder; - id.id = 0; - int slot = mHwcSlotGenerator->getHwcCacheSlot(id); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot); - - client_cache_t idB; - idB.token = binder; - idB.id = 1; - slot = mHwcSlotGenerator->getHwcCacheSlot(idB); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot); - - slot = mHwcSlotGenerator->getHwcCacheSlot(idB); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot); - - slot = mHwcSlotGenerator->getHwcCacheSlot(id); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot); -} - -TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) { - sp<IBinder> binder = new BBinder(); - std::vector<client_cache_t> ids; - uint32_t cacheId = 0; - // fill up cache - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - client_cache_t id; - id.token = binder; - id.id = cacheId; - ids.push_back(id); - - int slot = mHwcSlotGenerator->getHwcCacheSlot(id); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); - cacheId++; - } - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); - } - - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - client_cache_t id; - id.token = binder; - id.id = cacheId; - int slot = mHwcSlotGenerator->getHwcCacheSlot(id); - EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); - cacheId++; - } -} -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index fbc532eb77..06b9caa7cb 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -37,10 +37,7 @@ #include <system/window.h> #include <utils/String8.h> -#include "BufferQueueLayer.h" -#include "ContainerLayer.h" #include "DisplayRenderArea.h" -#include "EffectLayer.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" @@ -131,13 +128,15 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -179,11 +178,11 @@ public: sp<DisplayDevice> mDisplay; sp<DisplayDevice> mExternalDisplay; sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - new compositionengine::mock::DisplaySurface(); - mock::NativeWindow* mNativeWindow = new mock::NativeWindow(); + sp<compositionengine::mock::DisplaySurface>::make(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); std::vector<sp<Layer>> mAuxiliaryLayers; - sp<GraphicBuffer> mBuffer = new GraphicBuffer(); + sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(); ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); Hwc2::mock::Composer* mComposer = nullptr; @@ -245,8 +244,8 @@ void CompositionTest::captureScreenComposition() { HAL_PIXEL_FORMAT_RGBA_8888, 1, usage); - auto future = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer, - forSystem, regionSampling); + auto future = mFlinger.renderScreenImpl(std::move(renderArea), traverseLayers, + mCaptureScreenBuffer, forSystem, regionSampling); ASSERT_TRUE(future.valid()); const auto fenceResult = future.get(); @@ -309,16 +308,20 @@ struct BaseDisplayVariant { compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), ceDisplayArgs); + constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal; + constexpr bool kIsPrimary = true; + test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, - true /* isPrimary */) + kDisplayConnectionType, HWC_DISPLAY, kIsPrimary) .setDisplaySurface(test->mDisplaySurface) .setNativeWindow(test->mNativeWindow) .setSecure(Derived::IS_SECURE) .setPowerMode(Derived::INIT_POWER_MODE) .inject(); - Mock::VerifyAndClear(test->mNativeWindow); - test->mDisplay->setLayerStack(LAYER_STACK); + Mock::VerifyAndClear(test->mNativeWindow.get()); + + constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal; + test->mDisplay->setLayerFilter({LAYER_STACK, kIsInternal}); } template <typename Case> @@ -351,15 +354,13 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { + const bool, base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); - return futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); + return futureOf<FenceResult>(Fence::NO_FENCE); }); } @@ -404,16 +405,14 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&) - -> std::future<renderengine::RenderEngineResult> { + const bool, base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace); - return futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); + return futureOf<FenceResult>(Fence::NO_FENCE); }); } @@ -492,65 +491,31 @@ struct BaseLayerProperties { static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::PREMULTIPLIED; - static void enqueueBuffer(CompositionTest*, sp<BufferQueueLayer> layer) { - auto producer = layer->getProducer(); - - IGraphicBufferProducer::QueueBufferOutput qbo; - status_t result = producer->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &qbo); - if (result != NO_ERROR) { - ALOGE("Failed to connect() (%d)", result); - return; - } - - int slot; - sp<Fence> fence; - result = producer->dequeueBuffer(&slot, &fence, LayerProperties::WIDTH, - LayerProperties::HEIGHT, LayerProperties::FORMAT, - LayerProperties::USAGE, nullptr, nullptr); - if (result != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { - ALOGE("Failed to dequeueBuffer() (%d)", result); - return; - } - - sp<GraphicBuffer> buffer; - result = producer->requestBuffer(slot, &buffer); - if (result != NO_ERROR) { - ALOGE("Failed to requestBuffer() (%d)", result); - return; - } - - IGraphicBufferProducer::QueueBufferInput qbi(systemTime(), false /* isAutoTimestamp */, - LayerProperties::DATASPACE, - Rect(LayerProperties::WIDTH, - LayerProperties::HEIGHT), - LayerProperties::SCALING_MODE, - LayerProperties::TRANSFORM, Fence::NO_FENCE); - result = producer->queueBuffer(slot, qbi, &qbo); - if (result != NO_ERROR) { - ALOGE("Failed to queueBuffer (%d)", result); - return; - } - } - - static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) { - // TODO: Eliminate the complexity of actually creating a buffer - layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT); - status_t err = - layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::FORMAT); - ASSERT_EQ(NO_ERROR, err); + static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) { Mock::VerifyAndClear(test->mRenderEngine); - EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1); - enqueueBuffer(test, layer); - Mock::VerifyAndClearExpectations(test->mFlinger.scheduler()); + const auto buffer = std::make_shared< + renderengine::mock::FakeExternalTexture>(LayerProperties::WIDTH, + LayerProperties::HEIGHT, + DEFAULT_TEXTURE_ID, + LayerProperties::FORMAT, + LayerProperties::USAGE | + GraphicBuffer::USAGE_HW_TEXTURE); + + auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); + layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + layerDrawingState.buffer = buffer; + layerDrawingState.acquireFence = Fence::NO_FENCE; + layerDrawingState.dataspace = ui::Dataspace::UNKNOWN; + layer->setSurfaceDamageRegion( + Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH))); bool ignoredRecomputeVisibleRegions; - layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0); + layer->latchBuffer(ignoredRecomputeVisibleRegions, 0); Mock::VerifyAndClear(test->mRenderEngine); } - static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + static void setupLayerState(CompositionTest* test, sp<Layer> layer) { setupLatchedBuffer(test, layer); } @@ -640,7 +605,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -648,9 +613,7 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. - std::future<renderengine::RenderEngineResult> resultFuture = - futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); + std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREBufferCompositionCommonCallExpectations " @@ -693,7 +656,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -701,9 +664,7 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<renderengine::RenderEngineResult> resultFuture = - futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); + std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " @@ -738,7 +699,7 @@ struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerPropert using Base = BaseLayerProperties<SidebandLayerProperties>; static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE; - static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + static void setupLayerState(CompositionTest* test, sp<Layer> layer) { sp<NativeHandle> stream = NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); @@ -774,7 +735,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -782,9 +743,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<renderengine::RenderEngineResult> resultFuture = - futureOf<renderengine::RenderEngineResult>( - {NO_ERROR, base::unique_fd()}); + std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupInsecureREBufferCompositionCommonCallExpectations " @@ -821,14 +780,14 @@ struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerPro struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> { using Base = BaseLayerProperties<CursorLayerProperties>; - static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + static void setupLayerState(CompositionTest* test, sp<Layer> layer) { Base::setupLayerState(test, layer); test->mFlinger.setLayerPotentialCursor(layer, true); } }; struct NoLayerVariant { - using FlingerLayerType = sp<BufferQueueLayer>; + using FlingerLayerType = sp<Layer>; static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); } static void injectLayer(CompositionTest*, FlingerLayerType) {} @@ -862,8 +821,6 @@ struct BaseLayerVariant { static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) { auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); layerDrawingState.layerStack = LAYER_STACK; - layerDrawingState.width = 100; - layerDrawingState.height = 100; layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], LayerProperties::COLOR[2], LayerProperties::COLOR[3]); layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */); @@ -899,13 +856,13 @@ struct BaseLayerVariant { template <typename LayerProperties> struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<EffectLayer>; + using FlingerLayerType = sp<Layer>; static FlingerLayerType createLayer(CompositionTest* test) { - FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() { - return new EffectLayer( - LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer", - LayerProperties::LAYER_FLAGS, LayerMetadata())); + FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() { + return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), + "test-layer", LayerProperties::LAYER_FLAGS, + LayerMetadata())); }); auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); @@ -935,17 +892,17 @@ struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { template <typename LayerProperties> struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<BufferQueueLayer>; + using FlingerLayerType = sp<Layer>; static FlingerLayerType createLayer(CompositionTest* test) { test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID); FlingerLayerType layer = - Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() { + Base::template createLayerWithFactory<Layer>(test, [test]() { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", LayerProperties::LAYER_FLAGS, LayerMetadata()); args.textureName = test->mFlinger.mutableTexturePool().back(); - return new BufferQueueLayer(args); + return sp<Layer>::make(args); }); LayerProperties::setupLayerState(test, layer); @@ -987,12 +944,12 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { template <typename LayerProperties> struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<ContainerLayer>; + using FlingerLayerType = sp<Layer>; static FlingerLayerType createLayer(CompositionTest* test) { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer", LayerProperties::LAYER_FLAGS, LayerMetadata()); - FlingerLayerType layer = new ContainerLayer(args); + FlingerLayerType layer = sp<Layer>::make(args); Base::template initLayerDrawingStateAndComputeBounds(test, layer); return layer; } diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index 93af225b3e..60ad7a3a03 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #include "DisplayTransactionTestHelpers.h" +#include "mock/MockFrameRateMode.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -29,6 +30,7 @@ using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjec class InitiateModeChangeTest : public DisplayTransactionTest { public: + using Action = DisplayDevice::DesiredActiveModeAction; using Event = scheduler::DisplayModeEvent; void SetUp() override { @@ -42,6 +44,7 @@ public: PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + mFlinger.configureAndCommit(); mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60) @@ -55,31 +58,42 @@ protected: static constexpr DisplayModeId kModeId90{1}; static constexpr DisplayModeId kModeId120{2}; - static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz); - static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz); - static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz); + static inline const ftl::NonNull<DisplayModePtr> kMode60 = + ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode120 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz)); }; TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) { - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None})); + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None})); EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); } TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); - // Setting another mode should be cached but return false - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None})); + // Setting another mode should be cached but return None + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); } TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); mDisplay->clearDesiredActiveModeState(); @@ -87,9 +101,11 @@ TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) { } TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -100,18 +116,27 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); mDisplay->clearDesiredActiveModeState(); ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); } +TEST_F(InitiateModeChangeTest, initiateRenderRateChange) { + EXPECT_EQ(Action::InitiateRenderRateSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None})); + EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged) NO_THREAD_SAFETY_ANALYSIS { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -122,21 +147,23 @@ NO_THREAD_SAFETY_ANALYSIS { EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None})); + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); mDisplay->clearDesiredActiveModeState(); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index f04221cc21..e0b508aa73 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -36,8 +36,7 @@ DisplayTransactionTest::DisplayTransactionTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - // Default to no wide color display support configured - mFlinger.mutableHasWideColorDisplay() = false; + mFlinger.mutableSupportsWideColor() = false; mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) { @@ -51,7 +50,6 @@ DisplayTransactionTest::DisplayTransactionTest() { injectMockScheduler(); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.mutableInterceptor() = mSurfaceInterceptor; injectMockComposer(0); } @@ -66,13 +64,15 @@ DisplayTransactionTest::~DisplayTransactionTest() { void DisplayTransactionTest::injectMockScheduler() { EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mEventThread, createEventConnection(_, _)) - .WillOnce(Return( - new EventThreadConnection(mEventThread, /*callingUid=*/0, ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(mEventThread, + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) - .WillOnce(Return( - new EventThreadConnection(mSFEventThread, /*callingUid=*/0, ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread, + mock::EventThread::kCallingUid, + ResyncCallback()))); mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController), std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker), @@ -100,8 +100,8 @@ void DisplayTransactionTest::injectFakeBufferQueueFactory() { // This setup is only expected once per test. ASSERT_TRUE(mConsumer == nullptr && mProducer == nullptr); - mConsumer = new mock::GraphicBufferConsumer(); - mProducer = new mock::GraphicBufferProducer(); + mConsumer = sp<mock::GraphicBufferConsumer>::make(); + mProducer = sp<mock::GraphicBufferProducer>::make(); mFlinger.setCreateBufferQueueFunction([this](auto outProducer, auto outConsumer, bool) { *outProducer = mProducer; @@ -120,53 +120,13 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { }); } -sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( - std::function<void(FakeDisplayDeviceInjector&)> injectExtra) { - constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); - constexpr int DEFAULT_DISPLAY_WIDTH = 1080; - constexpr int DEFAULT_DISPLAY_HEIGHT = 1920; - constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0; - - // The DisplayDevice is required to have a framebuffer (behind the - // ANativeWindow interface) which uses the actual hardware display - // size. - EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0))); - EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0))); - EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)); - EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)); - EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)); - EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber()); - - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(&mPowerAdvisor) - .build()); - - constexpr bool kIsPrimary = true; - auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, - DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary); - - injector.setNativeWindow(mNativeWindow); - if (injectExtra) { - injectExtra(injector); - } - - auto displayDevice = injector.inject(); - - Mock::VerifyAndClear(mNativeWindow.get()); +bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const { + const auto& map = mFlinger.hwcPhysicalDisplayIdMap(); - return displayDevice; -} + const auto it = map.find(hwcDisplayId); + if (it == map.end()) return false; -bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const { - return mFlinger.hwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1; + return mFlinger.hwcDisplayData().count(it->second) == 1; } bool DisplayTransactionTest::hasTransactionFlagSet(int32_t flag) const { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index f5235ce953..d58e644506 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -42,6 +42,7 @@ #include <renderengine/mock/RenderEngine.h> #include <ui/DebugUtils.h> +#include "FakeDisplayInjector.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" @@ -49,7 +50,6 @@ #include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockNativeWindowSurface.h" -#include "mock/MockSurfaceInterceptor.h" #include "mock/MockVsyncController.h" #include "mock/system/window/MockNativeWindow.h" @@ -89,8 +89,11 @@ public: void injectMockComposer(int virtualDisplayCount); void injectFakeBufferQueueFactory(); void injectFakeNativeWindowSurfaceFactory(); + sp<DisplayDevice> injectDefaultInternalDisplay( - std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>); + std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) { + return mFakeDisplayInjector.injectInternalDisplay(injectExtra); + } // -------------------------------------------------------------------- // Postcondition helpers @@ -111,16 +114,17 @@ public: // Test instances TestableSurfaceFlinger mFlinger; - sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow(); - sp<GraphicBuffer> mBuffer = new GraphicBuffer(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); + sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(); Hwc2::mock::PowerAdvisor mPowerAdvisor; + FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow}; + // These mocks are created by the test, but are destroyed by SurfaceFlinger // by virtue of being stored into a std::unique_ptr. However we still need // to keep a reference to them for use in setting up call expectations. renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = nullptr; - sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor; mock::VsyncController* mVsyncController = new mock::VsyncController; mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker; @@ -355,6 +359,7 @@ struct HwcDisplayVariant { } // Called by tests to inject a HWC display setup + template <bool kInitPowerMode = true> static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { const auto displayId = DisplayVariant::DISPLAY_ID::get(); ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId)); @@ -363,18 +368,21 @@ struct HwcDisplayVariant { .setHwcDisplayId(HWC_DISPLAY_ID) .setResolution(DisplayVariant::RESOLUTION) .setActiveConfig(HWC_ACTIVE_CONFIG_ID) - .setPowerMode(INIT_POWER_MODE) + .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt) .inject(&test->mFlinger, test->mComposer); } // Called by tests to inject a HWC display setup + template <bool kInitPowerMode = true> static void injectHwcDisplay(DisplayTransactionTest* test) { EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) - .WillOnce(Return(Error::NONE)); - injectHwcDisplayWithNoDefaultCapabilities(test); + if constexpr (kInitPowerMode) { + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) + .WillOnce(Return(Error::NONE)); + } + injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test); } static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( @@ -434,14 +442,18 @@ struct HwcDisplayVariant { } } + template <bool kFailedHotplug = false> static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) { - constexpr auto CONNECTION_TYPE = - PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal - ? IComposerClient::DisplayConnectionType::INTERNAL - : IComposerClient::DisplayConnectionType::EXTERNAL; - - EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE))); + if constexpr (!kFailedHotplug) { + constexpr auto CONNECTION_TYPE = + PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal + ? IComposerClient::DisplayConnectionType::INTERNAL + : IComposerClient::DisplayConnectionType::EXTERNAL; + + EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), + Return(hal::V2_4::Error::NONE))); + } EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) .WillOnce(Return(hal::Error::NONE)); @@ -675,12 +687,10 @@ struct WideColorNotSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = false; static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableHasWideColorDisplay() = true; + test->mFlinger.mutableSupportsWideColor() = true; } static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE))); EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); } }; @@ -692,12 +702,11 @@ struct WideColorSupportNotConfiguredVariant { static constexpr bool WIDE_COLOR_SUPPORTED = false; static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableHasWideColorDisplay() = false; + test->mFlinger.mutableSupportsWideColor() = false; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0); EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0); EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); } diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index c033af8645..dd87f9dadf 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -72,7 +72,7 @@ protected: public: MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid, ResyncCallback&& resyncCallback, - ISurfaceComposer::EventRegistrationFlags eventRegistration) + EventRegistrationFlags eventRegistration) : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback), eventRegistration) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); @@ -85,16 +85,14 @@ protected: ~EventThreadTest() override; void createThread(std::unique_ptr<VSyncSource>); - sp<MockEventThreadConnection> createConnection( - ConnectionEventRecorder& recorder, - ISurfaceComposer::EventRegistrationFlags eventRegistration = {}, - uid_t ownerUid = mConnectionUid); + sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, + EventRegistrationFlags eventRegistration = {}, + uid_t ownerUid = mConnectionUid); void expectVSyncSetEnabledCallReceived(bool expectedState); void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration); VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); - void expectInterceptCallReceived(nsecs_t expectedTimestamp); void expectVsyncEventReceivedByConnection(const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount); @@ -115,7 +113,6 @@ protected: AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)> mVSyncSetDurationCallRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; - AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; @@ -149,11 +146,12 @@ EventThreadTest::EventThreadTest() { .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable())); createThread(std::move(vsyncSource)); - mConnection = createConnection(mConnectionEventCallRecorder, - ISurfaceComposer::EventRegistration::modeChanged | - ISurfaceComposer::EventRegistration::frameRateOverride); + mConnection = + createConnection(mConnectionEventCallRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride); mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder, - ISurfaceComposer::EventRegistration::modeChanged, + gui::ISurfaceComposer::EventRegistration::modeChanged, mThrottledConnectionUid); // A display must be connected for VSYNC events to be delivered. @@ -181,7 +179,6 @@ void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(), - mInterceptVSyncCallRecorder.getInvocable(), throttleVsync, getVsyncPeriod); // EventThread should register itself as VSyncSource callback. @@ -190,11 +187,12 @@ void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { } sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( - ConnectionEventRecorder& recorder, - ISurfaceComposer::EventRegistrationFlags eventRegistration, uid_t ownerUid) { + ConnectionEventRecorder& recorder, EventRegistrationFlags eventRegistration, + uid_t ownerUid) { sp<MockEventThreadConnection> connection = - new MockEventThreadConnection(mThread.get(), ownerUid, - mResyncCallRecorder.getInvocable(), eventRegistration); + sp<MockEventThreadConnection>::make(mThread.get(), ownerUid, + mResyncCallRecorder.getInvocable(), + eventRegistration); EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); return connection; } @@ -218,12 +216,6 @@ VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr; } -void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) { - auto args = mInterceptVSyncCallRecorder.waitForCall(); - ASSERT_TRUE(args.has_value()); - EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); -} - void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) { auto args = mThrottleVsyncCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); @@ -347,7 +339,6 @@ TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) { EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value()); - EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value()); } @@ -373,17 +364,15 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { expectVSyncSetEnabledCallReceived(true); // Use the received callback to signal a first vsync event. - // The interceptor should receive the event, as well as the connection. + // The throttler should receive the event, as well as the connection. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // Use the received callback to signal a second vsync event. - // The interceptor should receive the event, but the connection should + // The throttler should receive the event, but the connection should // not as it was only interested in the first. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); @@ -399,10 +388,9 @@ TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) { expectVSyncSetEnabledCallReceived(true); // Use the received callback to signal a vsync event. - // The interceptor should receive the event, as well as the connection. + // The throttler should receive the event, as well as the connection. VSyncSource::VSyncData vsyncData = {456, 789}; mCallback->onVSyncEvent(123, vsyncData); - expectInterceptCallReceived(123); expectVsyncEventFrameTimelinesCorrect(123, vsyncData); } @@ -476,10 +464,9 @@ TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { expectVSyncSetEnabledCallReceived(true); // Send a vsync event. EventThread should then make a call to the - // interceptor, and the second connection. The first connection should not + // the second connection. The first connection should not // get the event. mCallback->onVSyncEvent(123, {456, 0}); - expectInterceptCallReceived(123); EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value()); expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123, 1u); @@ -492,21 +479,18 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { expectVSyncSetEnabledCallReceived(true); // Send a vsync event. EventThread should then make a call to the - // interceptor, and the connection. + // throttler, and the connection. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // A second event should go to the same places. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); // A third event should go to the same places. mCallback->onVSyncEvent(789, {777, 111}); - expectInterceptCallReceived(789); expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); } @@ -517,27 +501,23 @@ TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); - // The first event will be seen by the interceptor, and not the connection. + // The first event will not be seen by the connection. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); - // The second event will be seen by the interceptor and the connection. + // The second event will be seen by the connection. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); expectVsyncEventReceivedByConnection(456, 2u); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); - // The third event will be seen by the interceptor, and not the connection. + // The third event will not be seen by the connection. mCallback->onVSyncEvent(789, {777, 744}); - expectInterceptCallReceived(789); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); - // The fourth event will be seen by the interceptor and the connection. + // The fourth event will be seen by the connection. mCallback->onVSyncEvent(101112, {7847, 86}); - expectInterceptCallReceived(101112); expectVsyncEventReceivedByConnection(101112, 4u); } @@ -550,9 +530,8 @@ TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { // Destroy the only (strong) reference to the connection. mConnection = nullptr; - // The first event will be seen by the interceptor, and not the connection. + // The first event will not be seen by the connection. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); // EventThread should disable vsync callbacks @@ -567,16 +546,12 @@ TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); - // The first event will be seen by the interceptor, and by the connection, - // which then returns an error. + // The first event will be seen by the connection, which then returns an error. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); - // A subsequent event will be seen by the interceptor and not by the - // connection. + // A subsequent event will not be seen by the connection. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value()); // EventThread should disable vsync callbacks with the second event @@ -598,10 +573,8 @@ TEST_F(EventThreadTest, tracksEventConnections) { // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); - // The first event will be seen by the interceptor, and by the connection, - // which then returns an error. + // The first event will be seen by the connection, which then returns an error. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123, 1u); @@ -616,16 +589,13 @@ TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); - // The first event will be seen by the interceptor, and by the connection, - // which then returns an non-fatal error. + // The first event will be seen by the connection, which then returns a non-fatal error. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); - // A subsequent event will be seen by the interceptor, and by the connection, - // which still then returns an non-fatal error. + // A subsequent event will be seen by the connection, which still then returns a non-fatal + // error. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u); // EventThread will not disable vsync callbacks as the errors are non-fatal. @@ -663,9 +633,10 @@ TEST_F(EventThreadTest, postConfigChangedPrimary) { .setId(DisplayModeId(7)) .setVsyncPeriod(16666666) .build(); + const Fps fps = mode->getFps() / 2; - mThread->onModeChanged(mode); - expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666); + mThread->onModeChanged({fps, ftl::as_non_null(mode)}); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs()); } TEST_F(EventThreadTest, postConfigChangedExternal) { @@ -674,9 +645,10 @@ TEST_F(EventThreadTest, postConfigChangedExternal) { .setId(DisplayModeId(5)) .setVsyncPeriod(16666666) .build(); + const Fps fps = mode->getFps() / 2; - mThread->onModeChanged(mode); - expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666); + mThread->onModeChanged({fps, ftl::as_non_null(mode)}); + expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs()); } TEST_F(EventThreadTest, postConfigChangedPrimary64bit) { @@ -685,8 +657,9 @@ TEST_F(EventThreadTest, postConfigChangedPrimary64bit) { .setId(DisplayModeId(7)) .setVsyncPeriod(16666666) .build(); - mThread->onModeChanged(mode); - expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666); + const Fps fps = mode->getFps() / 2; + mThread->onModeChanged({fps, ftl::as_non_null(mode)}); + expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs()); } TEST_F(EventThreadTest, suppressConfigChanged) { @@ -699,9 +672,10 @@ TEST_F(EventThreadTest, suppressConfigChanged) { .setId(DisplayModeId(9)) .setVsyncPeriod(16666666) .build(); + const Fps fps = mode->getFps() / 2; - mThread->onModeChanged(mode); - expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666); + mThread->onModeChanged({fps, ftl::as_non_null(mode)}); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs()); auto args = suppressConnectionEventRecorder.waitForCall(); ASSERT_FALSE(args.has_value()); @@ -747,17 +721,15 @@ TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { expectVSyncSetEnabledCallReceived(true); // Use the received callback to signal a first vsync event. - // The interceptor should receive the event, but not the connection. + // The throttler should receive the event, but not the connection. mCallback->onVSyncEvent(123, {456, 789}); - expectInterceptCallReceived(123); expectThrottleVsyncReceived(456, mThrottledConnectionUid); mThrottledConnectionEventCallRecorder.waitForUnexpectedCall(); // Use the received callback to signal a second vsync event. - // The interceptor should receive the event, but the connection should + // The throttler should receive the event, but the connection should // not as it was only interested in the first. mCallback->onVSyncEvent(456, {123, 0}); - expectInterceptCallReceived(456); expectThrottleVsyncReceived(123, mThrottledConnectionUid); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h new file mode 100644 index 0000000000..6e4bf2b06e --- /dev/null +++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h @@ -0,0 +1,96 @@ +/* + * Copyright 2022 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 "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { + +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; +using android::hardware::graphics::composer::hal::HWDisplayId; +using android::Hwc2::mock::PowerAdvisor; + +struct FakeDisplayInjectorArgs { + PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u); + HWDisplayId hwcDisplayId = 0; + bool isPrimary = true; +}; + +class FakeDisplayInjector { +public: + FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor, + sp<mock::NativeWindow> nativeWindow) + : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {} + + sp<DisplayDevice> injectInternalDisplay( + const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra, + FakeDisplayInjectorArgs args = {}) { + using testing::_; + using testing::AnyNumber; + using testing::DoAll; + using testing::Mock; + using testing::Return; + using testing::SetArgPointee; + + constexpr ui::Size kResolution = {1080, 1920}; + + // The DisplayDevice is required to have a framebuffer (behind the + // ANativeWindow interface) which uses the actual hardware display + // size. + EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getWidth()), Return(0))); + EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getHeight()), Return(0))); + EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)); + EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)); + EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)); + EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber()); + + auto compositionDisplay = compositionengine::impl:: + createDisplay(mFlinger.getCompositionEngine(), + compositionengine::DisplayCreationArgsBuilder() + .setId(args.displayId) + .setPixels(kResolution) + .setPowerAdvisor(&mPowerAdvisor) + .build()); + + auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, + args.hwcDisplayId, args.isPrimary); + + injector.setNativeWindow(mNativeWindow); + if (injectExtra) { + injectExtra(injector); + } + + auto displayDevice = injector.inject(); + + Mock::VerifyAndClear(mNativeWindow.get()); + + return displayDevice; + } + + TestableSurfaceFlinger& mFlinger; + Hwc2::mock::PowerAdvisor& mPowerAdvisor; + sp<mock::NativeWindow> mNativeWindow; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index bb1f4328b5..1cd9e49051 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -24,9 +24,6 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" -#include "EffectLayer.h" #include "FpsReporter.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" @@ -51,6 +48,7 @@ using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using gui::LayerMetadata; struct TestableFpsListener : public gui::BnFpsListener { TestableFpsListener() {} @@ -80,7 +78,7 @@ protected: static constexpr int32_t PRIORITY_UNSET = -1; void setupScheduler(); - sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata); + sp<Layer> createBufferStateLayer(LayerMetadata metadata); TestableSurfaceFlinger mFlinger; mock::FrameTimeline mFrameTimeline = @@ -95,8 +93,8 @@ protected: sp<TestableFpsListener> mFpsListener; fake::FakeClock* mClock = new fake::FakeClock(); - sp<FpsReporter> mFpsReporter = - new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock)); + sp<FpsReporter> mFpsReporter = sp<FpsReporter>::make(mFrameTimeline, *(mFlinger.flinger()), + std::unique_ptr<Clock>(mClock)); }; FpsReporterTest::FpsReporterTest() { @@ -107,7 +105,7 @@ FpsReporterTest::FpsReporterTest() { setupScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - mFpsListener = new TestableFpsListener(); + mFpsListener = sp<TestableFpsListener>::make(); } FpsReporterTest::~FpsReporterTest() { @@ -116,10 +114,10 @@ FpsReporterTest::~FpsReporterTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { +sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { sp<Client> client; LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); - return new BufferStateLayer(args); + return sp<Layer>::make(args); } void FpsReporterTest::setupScheduler() { @@ -128,13 +126,15 @@ void FpsReporterTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -153,7 +153,7 @@ TEST_F(FpsReporterTest, callsListeners) { mParent = createBufferStateLayer(); constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; - targetMetadata.setInt32(METADATA_TASK_ID, kTaskId); + targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); mTarget = createBufferStateLayer(targetMetadata); mChild = createBufferStateLayer(); mGrandChild = createBufferStateLayer(); @@ -188,7 +188,7 @@ TEST_F(FpsReporterTest, callsListeners) { TEST_F(FpsReporterTest, rateLimits) { const constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; - targetMetadata.setInt32(METADATA_TASK_ID, kTaskId); + targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); mTarget = createBufferStateLayer(targetMetadata); mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp index 1e6e3361b2..ac63a0edbd 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp @@ -21,8 +21,6 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> -#include "BufferStateLayer.h" -#include "EffectLayer.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" @@ -59,8 +57,8 @@ protected: static constexpr int32_t PRIORITY_UNSET = -1; void setupScheduler(); - sp<BufferStateLayer> createBufferStateLayer(); - sp<EffectLayer> createEffectLayer(); + sp<Layer> createBufferStateLayer(); + sp<Layer> createEffectLayer(); void setParent(Layer* child, Layer* parent); void commitTransaction(Layer* layer); @@ -88,22 +86,21 @@ RefreshRateSelectionTest::~RefreshRateSelectionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - -sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() { +sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() { sp<Client> client; LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS, LayerMetadata()); - return new BufferStateLayer(args); + return sp<Layer>::make(args); } -sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() { +sp<Layer> RefreshRateSelectionTest::createEffectLayer() { sp<Client> client; LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); - return new EffectLayer(args); + return sp<Layer>::make(args); } void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) { - child->setParent(parent); + child->setParent(sp<Layer>::fromExisting(parent)); } void RefreshRateSelectionTest::commitTransaction(Layer* layer) { @@ -117,13 +114,15 @@ void RefreshRateSelectionTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index f1efa9286d..f47ac6dc43 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -217,9 +217,12 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) { TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); flushTokens(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired); @@ -227,9 +230,12 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) { int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid); @@ -239,9 +245,12 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) { TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) { int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); constexpr int32_t inputEventId = 1; + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = inputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId()); @@ -250,9 +259,12 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) { TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); // Set up the display frame @@ -278,14 +290,17 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdTwo, sLayerNameTwo, - sLayerNameTwo, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdTwo, + sLayerNameTwo, sLayerNameTwo, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); @@ -324,9 +339,11 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); int64_t sfToken = mTokenManager->generateTokenForPredictions( {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, - sPidOne, sUidOne, sLayerIdOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11)); @@ -347,10 +364,13 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); int64_t sfToken = mTokenManager->generateTokenForPredictions( {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); @@ -442,11 +462,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setAcquireFenceTime(20); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -457,7 +480,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) { addEmptyDisplayFrame(); auto displayFrame0 = getDisplayFrame(0); - EXPECT_EQ(displayFrame0->getActuals().presentTime, -1); + EXPECT_EQ(displayFrame0->getActuals().presentTime, 59); EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown); EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown); @@ -470,11 +493,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = -1; int64_t sfToken1 = -1; + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setAcquireFenceTime(20); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -495,11 +521,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setAcquireFenceTime(20); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -521,11 +550,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) { auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setAcquireFenceTime(20); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -546,11 +578,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); surfaceFrame1->setAcquireFenceTime(20); @@ -570,11 +605,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(45); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); @@ -596,11 +634,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({40, 60, 92}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(50); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); @@ -622,11 +663,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(40); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); @@ -648,11 +692,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 58}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(40); mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate); @@ -676,11 +723,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(45); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); @@ -706,11 +756,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(45); // Trigger a prediction expiry flushTokens(); @@ -744,9 +797,12 @@ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) { auto tracingSession = getTracingSessionForTest(); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); // Set up the display frame @@ -771,9 +827,12 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) { tracingSession->StartBlocking(); int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = token1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, - sLayerIdOne, sLayerNameOne, sLayerNameOne, + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, /*isBuffer*/ true, sGameMode); // Set up the display frame @@ -1133,14 +1192,18 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 25, 40}); int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({30, 35, 40}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = sInputEventId; + auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setActualQueueTime(10); surfaceFrame1->setDropTime(15); @@ -1293,10 +1356,13 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredIsAppMissedDeadline // Flush the token so that it would expire flushTokens(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = 0; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0}, - sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setActualQueueTime(appEndTime); surfaceFrame1->setAcquireFenceTime(appEndTime); @@ -1369,10 +1435,13 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced // Flush the token so that it would expire flushTokens(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = 0; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0}, - sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count(); constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count(); @@ -1438,10 +1507,13 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 30}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); @@ -1608,7 +1680,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame0->getActuals().presentTime, 52); EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); - EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed); + EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); // case 3 - cpu time = 86 - 82 = 4, vsync period = 30 mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30)); @@ -1654,10 +1726,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70}); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40}); int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(16); mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1674,10 +1749,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = surfaceFrameToken2; + ftInfo2.inputEventId = sInputEventId; auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame2->setAcquireFenceTime(36); mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1734,10 +1812,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70}); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40}); int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(16); mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1754,10 +1835,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = surfaceFrameToken2; + ftInfo2.inputEventId = sInputEventId; auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame2->setAcquireFenceTime(36); mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1813,10 +1897,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 50, 50}); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(40); mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1857,10 +1944,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 50, 50}); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30}); int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(26); mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1877,10 +1967,13 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = surfaceFrameToken2; + ftInfo2.inputEventId = sInputEventId; auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame2->setAcquireFenceTime(40); mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1932,10 +2025,13 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 120, 120}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(50); mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1952,10 +2048,13 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = surfaceFrameToken2; + ftInfo2.inputEventId = sInputEventId; auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame2->setAcquireFenceTime(84); mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54); @@ -2010,10 +2109,13 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = surfaceFrameToken1; + ftInfo.inputEventId = sInputEventId; auto surfaceFrame1 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame1->setAcquireFenceTime(50); mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -2030,10 +2132,13 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = surfaceFrameToken2; + ftInfo2.inputEventId = sInputEventId; auto surfaceFrame2 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerIdOne, sLayerNameOne, - sLayerNameOne, /*isBuffer*/ true, sGameMode); + mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); surfaceFrame2->setAcquireFenceTime(80); mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30)); // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84 @@ -2079,6 +2184,94 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing); } +TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_GpuAndCpuMiss) { + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); + int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + + // Case 1: cpu time = 33 - 12 = 21, vsync period = 11 + mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11)); + mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1); + auto displayFrame = getDisplayFrame(0); + gpuFence1->signalForTest(36); + presentFence1->signalForTest(52); + + // Fences haven't been flushed yet, so it should be 0 + EXPECT_EQ(displayFrame->getActuals().presentTime, 0); + + addEmptyDisplayFrame(); + displayFrame = getDisplayFrame(0); + + // Fences have flushed, so the present timestamps should be updated + EXPECT_EQ(displayFrame->getActuals().presentTime, 52); + EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent); + EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); + EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); + + // Case 2: No GPU fence so it will not use GPU composition. + mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30)); + mFrameTimeline->setSfPresent(66, presentFence2); + auto displayFrame2 = getDisplayFrame(2); // 2 because of previous empty frame + presentFence2->signalForTest(90); + + // Fences for the frame haven't been flushed yet, so it should be 0 + EXPECT_EQ(displayFrame2->getActuals().presentTime, 0); + + addEmptyDisplayFrame(); + + // Fences have flushed, so the present timestamps should be updated + EXPECT_EQ(displayFrame2->getActuals().presentTime, 90); + EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); + EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); + EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed); +} + +TEST_F(FrameTimelineTest, jankClassification_presentFenceError) { + auto erroneousPresentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto erroneousPresentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto validPresentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); + int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + int64_t sfToken3 = mTokenManager->generateTokenForPredictions({72, 80, 80}); + + mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); + mFrameTimeline->setSfPresent(26, erroneousPresentFence1); + + mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); + mFrameTimeline->setSfPresent(60, erroneousPresentFence2); + + mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11)); + mFrameTimeline->setSfPresent(80, validPresentFence); + + validPresentFence->signalForTest(80); + + addEmptyDisplayFrame(); + + { + auto displayFrame = getDisplayFrame(0); + EXPECT_EQ(displayFrame->getActuals().presentTime, 26); + EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent); + EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish); + EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown); + } + { + auto displayFrame = getDisplayFrame(1); + EXPECT_EQ(displayFrame->getActuals().presentTime, 60); + EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent); + EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish); + EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown); + } + { + auto displayFrame = getDisplayFrame(2); + EXPECT_EQ(displayFrame->getActuals().presentTime, 80); + EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); + EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); + EXPECT_EQ(displayFrame->getJankType(), JankType::None); + } +} + TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) { EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f); } @@ -2237,4 +2430,38 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f); } +TEST_F(FrameTimelineTest, getMinTime) { + // Use SurfaceFrame::getBaseTime to test the getMinTime. + FrameTimelineInfo ftInfo; + + // Valid prediction state test. + ftInfo.vsyncId = 0L; + mTokenManager->generateTokenForPredictions({10}); + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); + ASSERT_EQ(surfaceFrame->getBaseTime(), 10); + + // Test prediction state which is not valid. + ftInfo.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID; + surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne, + sLayerNameOne, sLayerNameOne, + /*isBuffer*/ true, sGameMode); + // Start time test. + surfaceFrame->setActualStartTime(200); + ASSERT_EQ(surfaceFrame->getBaseTime(), 200); + + // End time test. + surfaceFrame->setAcquireFenceTime(100); + ASSERT_EQ(surfaceFrame->getBaseTime(), 100); + + // Present time test. + auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); + mFrameTimeline->addSurfaceFrame(surfaceFrame); + presentFence->signalForTest(std::chrono::nanoseconds(50ns).count()); + mFrameTimeline->setSfPresent(50, presentFence); + ASSERT_EQ(surfaceFrame->getBaseTime(), 50); +} } // namespace android::frametimeline diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp index 981ca1d227..29aa7171ba 100644 --- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp +++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp @@ -34,6 +34,8 @@ using testing::_; using testing::Mock; using testing::Return; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using gui::GameMode; +using gui::LayerMetadata; class GameModeTest : public testing::Test { public: @@ -51,11 +53,10 @@ public: ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - sp<BufferStateLayer> createBufferStateLayer() { + sp<Layer> createLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, - LayerMetadata()); - return new BufferStateLayer(args); + LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata()); + return sp<Layer>::make(args); } void setupScheduler() { @@ -64,13 +65,15 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -92,7 +95,7 @@ public: // Mocks the behavior of applying a transaction from WMShell void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) { - mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode)); + mLayerMetadata.setInt32(gui::METADATA_GAME_MODE, static_cast<int32_t>(gameMode)); layer->setMetadata(mLayerMetadata); layer->setGameModeForTree(gameMode); } @@ -104,9 +107,9 @@ public: }; TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) { - sp<BufferStateLayer> rootLayer = createBufferStateLayer(); - sp<BufferStateLayer> childLayer1 = createBufferStateLayer(); - sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); + sp<Layer> rootLayer = createLayer(); + sp<Layer> childLayer1 = createLayer(); + sp<Layer> childLayer2 = createLayer(); rootLayer->addChild(childLayer1); rootLayer->addChild(childLayer2); rootLayer->setGameModeForTree(GameMode::Performance); @@ -117,8 +120,8 @@ TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) { } TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) { - sp<BufferStateLayer> rootLayer = createBufferStateLayer(); - sp<BufferStateLayer> childLayer = createBufferStateLayer(); + sp<Layer> rootLayer = createLayer(); + sp<Layer> childLayer = createLayer(); rootLayer->setGameModeForTree(GameMode::Performance); rootLayer->addChild(childLayer); @@ -127,8 +130,8 @@ TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) { } TEST_F(GameModeTest, RemoveChildResetsGameMode) { - sp<BufferStateLayer> rootLayer = createBufferStateLayer(); - sp<BufferStateLayer> childLayer = createBufferStateLayer(); + sp<Layer> rootLayer = createLayer(); + sp<Layer> childLayer = createLayer(); rootLayer->setGameModeForTree(GameMode::Performance); rootLayer->addChild(childLayer); @@ -140,9 +143,9 @@ TEST_F(GameModeTest, RemoveChildResetsGameMode) { } TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) { - sp<BufferStateLayer> rootLayer = createBufferStateLayer(); - sp<BufferStateLayer> childLayer1 = createBufferStateLayer(); - sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); + sp<Layer> rootLayer = createLayer(); + sp<Layer> childLayer1 = createLayer(); + sp<Layer> childLayer2 = createLayer(); rootLayer->setGameModeForTree(GameMode::Standard); rootLayer->addChild(childLayer1); diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 5241604cd0..8f89a8ca7a 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -59,27 +59,63 @@ using ::testing::Return; using ::testing::SetArgPointee; using ::testing::StrictMock; -TEST(HWComposerTest, isHeadless) { - Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>(); - impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - ASSERT_TRUE(hwc.isHeadless()); +struct HWComposerTest : testing::Test { + using HalError = hardware::graphics::composer::V2_1::Error; - const hal::HWDisplayId hwcId = 1; + Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>(); + impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _)) - .WillOnce(DoAll(SetArgPointee<2>(getExternalEdid()), - Return(hardware::graphics::composer::V2_1::Error::NONE))); + void expectHotplugConnect(hal::HWDisplayId hwcDisplayId) { + constexpr uint8_t kPort = 255; + EXPECT_CALL(*mHal, getDisplayIdentificationData(hwcDisplayId, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPort), + SetArgPointee<2>(getExternalEdid()), Return(HalError::NONE))); - EXPECT_CALL(*mHal, setVsyncEnabled(_, _)); - EXPECT_CALL(*mHal, setClientTargetSlotCount(_)); + EXPECT_CALL(*mHal, setClientTargetSlotCount(_)); + EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE)); + EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); + } +}; + +TEST_F(HWComposerTest, isHeadless) { + ASSERT_TRUE(mHwc.isHeadless()); + + constexpr hal::HWDisplayId kHwcDisplayId = 1; + expectHotplugConnect(kHwcDisplayId); + + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + ASSERT_FALSE(mHwc.isHeadless()); + + mHwc.disconnectDisplay(info->id); + ASSERT_TRUE(mHwc.isHeadless()); +} + +TEST_F(HWComposerTest, getActiveMode) { + // Unknown display. + EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt); - auto info = hwc.onHotplug(hwcId, hal::Connection::CONNECTED); + constexpr hal::HWDisplayId kHwcDisplayId = 2; + expectHotplugConnect(kHwcDisplayId); + + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - auto displayId = info->id; - ASSERT_FALSE(hwc.isHeadless()); - hwc.disconnectDisplay(displayId); - ASSERT_TRUE(hwc.isHeadless()); + { + // Display is known to SF but not HWC, e.g. the hotplug disconnect is pending. + EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) + .WillOnce(Return(HalError::BAD_DISPLAY)); + + EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt); + } + { + constexpr hal::HWConfigId kConfigId = 42; + EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) + .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE))); + + EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId); + } } struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { @@ -93,8 +129,7 @@ struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId)); }; -struct HWComposerSetCallbackTest : testing::Test { - Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>(); +struct HWComposerSetCallbackTest : HWComposerTest { MockHWC2ComposerCallback mCallback; }; @@ -111,12 +146,13 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { {kMetadata2Name, kMetadata2Mandatory}, }), Return(hardware::graphics::composer::V2_4::Error::NONE))); + EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE)); + EXPECT_CALL(*mHal, registerCallback(_)); - impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - hwc.setCallback(mCallback); + mHwc.setCallback(mCallback); - const auto& supported = hwc.getSupportedLayerGenericMetadata(); + const auto& supported = mHwc.getSupportedLayerGenericMetadata(); EXPECT_EQ(2u, supported.size()); EXPECT_EQ(1u, supported.count(kMetadata1Name)); EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second); @@ -128,13 +164,13 @@ TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadat EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{})); EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)) .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); + EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED)); EXPECT_CALL(*mHal, registerCallback(_)); - impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - hwc.setCallback(mCallback); + mHwc.setCallback(mCallback); - const auto& supported = hwc.getSupportedLayerGenericMetadata(); - EXPECT_EQ(0u, supported.size()); + const auto& supported = mHwc.getSupportedLayerGenericMetadata(); + EXPECT_TRUE(supported.empty()); } struct HWComposerLayerTest : public testing::Test { diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp new file mode 100644 index 0000000000..8560902e4e --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -0,0 +1,728 @@ +/* + * Copyright 2022 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "FrontEnd/LayerHandle.h" +#include "FrontEnd/LayerHierarchy.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "Layer.h" +#include "gui/SurfaceComposerClient.h" + +#define UPDATE_AND_VERIFY(HIERARCHY) \ + ({ \ + SCOPED_TRACE(""); \ + updateAndVerify((HIERARCHY)); \ + }) + +namespace android::surfaceflinger::frontend { + +namespace { +LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) { + LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + args.addToRoot = canBeRoot; + args.parentHandle = parent; + args.mirrorLayerHandle = mirror; + return args; +} +} // namespace + +// To run test: +/** + mp :libsurfaceflinger_unittest && adb sync; adb shell \ + /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \ + --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \ + --gtest_shuffle \ + --gtest_brief=1 +*/ + +class LayerHierarchyTest : public testing::Test { +protected: + LayerHierarchyTest() { + // tree with 3 levels of children + // ROOT + // ├── 1 + // │ ├── 11 + // │ │ └── 111 + // │ ├── 12 + // │ │ ├── 121 + // │ │ └── 122 + // │ │ └── 1221 + // │ └── 13 + // └── 2 + + createRootLayer(1); + createRootLayer(2); + createLayer(11, 1); + createLayer(12, 1); + createLayer(13, 1); + createLayer(111, 11); + createLayer(121, 12); + createLayer(122, 12); + createLayer(1221, 122); + mLifecycleManager.commitChanges(); + } + std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const { + std::vector<uint32_t> layerIds; + hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath&) -> bool { + layerIds.emplace_back(hierarchy.getLayer()->id); + return true; + }); + return layerIds; + } + + std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const { + std::vector<uint32_t> layerIds; + hierarchy.traverseInZOrder( + [&layerIds = layerIds](const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath&) -> bool { + layerIds.emplace_back(hierarchy.getLayer()->id); + return true; + }); + return layerIds; + } + + void createRootLayer(uint32_t id) { + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void createLayer(uint32_t id, uint32_t parentId) { + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId], + /*mirror=*/nullptr))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void reparentLayer(uint32_t id, uint32_t newParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + if (newParentId == UNASSIGNED_LAYER_ID) { + transactions.back().states.front().state.parentSurfaceControlForChild = nullptr; + } else { + auto parentHandle = mHandles[newParentId]; + transactions.back().states.front().state.parentSurfaceControlForChild = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, + static_cast<int32_t>(newParentId), "Test"); + } + transactions.back().states.front().state.what = layer_state_t::eReparent; + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + if (relativeParentId == UNASSIGNED_LAYER_ID) { + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + } else { + auto parentHandle = mHandles[relativeParentId]; + transactions.back().states.front().state.relativeLayerSurfaceControl = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, + static_cast<int32_t>(relativeParentId), "test"); + transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; + } + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) { + auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent]; + auto mirrorHandle = + (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror]; + + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle, + /*mirror=*/mHandles[layerToMirror]))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void updateBackgroundColor(uint32_t id, half alpha) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + transactions.back().states.front().state.bgColorAlpha = alpha; + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); } + + void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { + if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { + hierarchyBuilder.update(mLifecycleManager.getLayers(), + mLifecycleManager.getDestroyedLayers()); + } + mLifecycleManager.commitChanges(); + + // rebuild layer hierarchy from scratch and verify that it matches the updated state. + LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers()); + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), + getTraversalPath(newBuilder.getHierarchy())); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), + getTraversalPathInZOrder(newBuilder.getHierarchy())); + EXPECT_FALSE( + mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + } + + void setZ(uint32_t id, int32_t z) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().state.z = z; + mLifecycleManager.applyTransactions(transactions); + } + LayerLifecycleManager mLifecycleManager; + std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; +}; + +// reparenting tests +TEST_F(LayerHierarchyTest, addLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + createRootLayer(3); + createLayer(112, 11); + createLayer(12211, 1221); + UPDATE_AND_VERIFY(hierarchyBuilder); + expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, reparentLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentLayer(2, 11); + reparentLayer(111, 12); + reparentLayer(1221, 1); + reparentLayer(1221, 13); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, reparentLayerToNull) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + reparentLayer(2, UNASSIGNED_LAYER_ID); + reparentLayer(11, UNASSIGNED_LAYER_ID); + reparentLayer(1221, 13); + reparentLayer(1221, UNASSIGNED_LAYER_ID); + + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {2, 11, 111, 1221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentLayer(2, UNASSIGNED_LAYER_ID); + reparentLayer(11, UNASSIGNED_LAYER_ID); + reparentLayer(1221, UNASSIGNED_LAYER_ID); + + destroyLayerHandle(2); + destroyLayerHandle(11); + destroyLayerHandle(1221); + + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + destroyLayerHandle(111); + UPDATE_AND_VERIFY(hierarchyBuilder); + + // handle is destroyed but layer is kept alive and reachable by parent + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + // destroy parent layer and the child gets destroyed + reparentLayer(11, UNASSIGNED_LAYER_ID); + destroyLayerHandle(11); + UPDATE_AND_VERIFY(hierarchyBuilder); + + expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentLayer(11, UNASSIGNED_LAYER_ID); + reparentLayer(11, 1); + + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// offscreen tests +TEST_F(LayerHierarchyTest, layerMovesOnscreen) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + reparentLayer(11, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + reparentLayer(11, 1); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + reparentLayer(11, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + createLayer(112, 11); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {11, 111, 112}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// rel-z tests +TEST_F(LayerHierarchyTest, setRelativeParent) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + reparentRelativeLayer(11, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, reparentToRelativeParent) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + reparentLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, setParentAsRelativeParent) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + reparentRelativeLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(11, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + reparentLayer(2, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {2, 11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// mirror tests +TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, + 1221, 13, 14, 11, 111, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + reparentLayer(11, UNASSIGNED_LAYER_ID); + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) { + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); + mLifecycleManager.commitChanges(); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + createLayer(1111, 111); + createLayer(112, 11); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 1111, 112, 12, 121, 122, + 1221, 13, 14, 11, 111, 1111, 112, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// mirror & relatives tests +TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(111, 12); + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); + + // ROOT + // ├── 1 + // │ ├── 11 + // │ │ └── 111 + // │ ├── 12 + // │ │ ├── 121 + // │ │ ├── 122 + // │ │ │ └── 1221 + // │ │ └ - 111 (relative) + // │ ├── 13 + // │ └── 14 + // │ └ * 11 (mirroring) + // └── 2 + + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 111, 121, 122, + 1221, 13, 14, 11, 111, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + // 111 is not reachable in the mirror + expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(1221, 12); + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12); + + // ROOT + // ├── 1 + // │ ├── 11 + // │ │ └── 111 + // │ ├── 12 + // │ │ ├── 121 + // │ │ ├── 122 + // │ │ │ └── 1221 + // │ │ └ - 1221 (relative) + // │ ├── 13 + // │ └── 14 + // │ └ * 12 (mirroring) + // └── 2 + + UPDATE_AND_VERIFY(hierarchyBuilder); + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 1221, + 13, 14, 12, 121, 122, 1221, 1221, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + // relative layer 1221 is traversable in the mirrored hierarchy as well + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + reparentRelativeLayer(11, 2); + reparentLayer(2, UNASSIGNED_LAYER_ID); + destroyLayerHandle(2); + + UPDATE_AND_VERIFY(hierarchyBuilder); + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {11, 111}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + // remove relative parent so layer becomes onscreen again + reparentRelativeLayer(11, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentRelativeLayer(1221, 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + // verify relz path + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + // offscreen layer cannot be reached as a relative child + reparentLayer(12, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + expectedTraversalPath = {1, 11, 111, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {12, 121, 122, 1221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + // layer when onscreen can be reached as a relative child again + reparentLayer(12, 1); + UPDATE_AND_VERIFY(hierarchyBuilder); + + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + updateBackgroundColor(1, 0.5); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// cycle tests +TEST_F(LayerHierarchyTest, ParentBecomesTheChild) { + // remove default hierarchy + mLifecycleManager = LayerLifecycleManager(); + createRootLayer(1); + createLayer(11, 1); + reparentLayer(1, 11); + mLifecycleManager.commitChanges(); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + std::vector<uint32_t> expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, RelativeLoops) { + // remove default hierarchy + mLifecycleManager = LayerLifecycleManager(); + createRootLayer(1); + createRootLayer(2); + createLayer(11, 1); + reparentRelativeLayer(11, 2); + reparentRelativeLayer(2, 11); + mLifecycleManager.commitChanges(); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + // fix loop + uint32_t invalidRelativeRoot; + bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); + EXPECT_TRUE(hasRelZLoop); + mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); + hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); + EXPECT_EQ(invalidRelativeRoot, 11u); + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {11, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, IndirectRelativeLoops) { + // remove default hierarchy + mLifecycleManager = LayerLifecycleManager(); + createRootLayer(1); + createRootLayer(2); + createLayer(11, 1); + createLayer(111, 11); + createLayer(21, 2); + createLayer(22, 2); + createLayer(221, 22); + reparentRelativeLayer(22, 111); + reparentRelativeLayer(11, 221); + mLifecycleManager.commitChanges(); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + + // fix loop + uint32_t invalidRelativeRoot; + bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); + EXPECT_TRUE(hasRelZLoop); + mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); + hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 2, 21}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {11, 111, 22, 221}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) { + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + reparentLayer(1, UNASSIGNED_LAYER_ID); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) { + // remove default hierarchy + mLifecycleManager = LayerLifecycleManager(); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + createRootLayer(1); + destroyLayerHandle(1); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + +// traversal path test +TEST_F(LayerHierarchyTest, traversalPathId) { + setZ(122, -1); + LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + auto checkTraversalPathIdVisitor = + [](const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath& traversalPath) -> bool { + EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id); + return true; + }; + hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor); + hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor); +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 17511cd615..979924af58 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -69,11 +69,11 @@ protected: // LayerHistory::summarize makes no guarantee of the order of the elements in the summary // however, for testing only, a stable order is required, therefore we sort the list here. // Any tests requiring ordered results must create layers with names. - auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now); + auto summary = history().summarize(*mScheduler->refreshRateSelector(), now); std::sort(summary.begin(), summary.end(), - [](const RefreshRateConfigs::LayerRequirement& a, - const RefreshRateConfigs::LayerRequirement& b) -> bool { - return a.name < b.name; + [](const RefreshRateSelector::LayerRequirement& lhs, + const RefreshRateSelector::LayerRequirement& rhs) -> bool { + return lhs.name < rhs.name; }); return summary; } @@ -125,22 +125,70 @@ protected: ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate); } - std::shared_ptr<RefreshRateConfigs> mConfigs = - std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0), - LO_FPS), - createDisplayMode(DisplayModeId(1), - HI_FPS)), - DisplayModeId(0)); + std::shared_ptr<RefreshRateSelector> mSelector = + std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0), + LO_FPS), + createDisplayMode(DisplayModeId(1), + HI_FPS)), + DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); TestableSurfaceFlinger mFlinger; }; namespace { +TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) { + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + EXPECT_CALL(*layer, getDefaultFrameRateCompatibility()) + .WillOnce(Return(LayerInfo::FrameRateCompatibility::NoVote)); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + + nsecs_t time = systemTime(); + + // No layers returned if no layers are active. + EXPECT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0, activeLayerCount()); + + history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */); + + EXPECT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(1, activeLayerCount()); +} + +TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) { + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + EXPECT_CALL(*layer, getDefaultFrameRateCompatibility()) + .WillOnce(Return(LayerInfo::FrameRateCompatibility::Min)); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + + nsecs_t time = systemTime(); + + EXPECT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0, activeLayerCount()); + + history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */); + + auto summary = summarizeLayerHistory(time); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); +} + TEST_F(LayerHistoryTest, oneLayer) { const auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp new file mode 100644 index 0000000000..89440a689c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -0,0 +1,453 @@ +/* + * Copyright 2022 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "FrontEnd/LayerHandle.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "Layer.h" +#include "gui/SurfaceComposerClient.h" + +using namespace android::surfaceflinger; + +namespace android::surfaceflinger::frontend { + +namespace { +LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) { + LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + args.addToRoot = canBeRoot; + args.parentHandle = parent; + args.mirrorLayerHandle = mirror; + return args; +} +} // namespace + +// To run test: +/** + mp :libsurfaceflinger_unittest && adb sync; adb shell \ + /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \ + --gtest_filter="LayerLifecycleManagerTest.*" --gtest_repeat=100 \ + --gtest_shuffle \ + --gtest_brief=1 +*/ +class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener { +public: + void onLayerAdded(const RequestedLayerState& layer) override { + mActualLayersAdded.emplace(layer.id); + }; + void onLayerDestroyed(const RequestedLayerState& layer) override { + mActualLayersDestroyed.emplace(layer.id); + }; + + void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) { + EXPECT_EQ(expectedLayersAdded, mActualLayersAdded); + mActualLayersAdded.clear(); + } + void expectLayersDestroyed(const std::unordered_set<uint32_t>& expectedLayersDestroyed) { + EXPECT_EQ(expectedLayersDestroyed, mActualLayersDestroyed); + mActualLayersDestroyed.clear(); + } + + std::unordered_set<uint32_t> mActualLayersAdded; + std::unordered_set<uint32_t> mActualLayersDestroyed; +}; + +class LayerLifecycleManagerTest : public testing::Test { +protected: + std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) { + return std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)); + } + + std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) { + mHandles[parentId] = sp<LayerHandle>::make(parentId); + return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false, + /*parent=*/mHandles[parentId], + /*mirror=*/nullptr)); + } + + TransactionState reparentLayer(uint32_t id, uint32_t newParentId) { + TransactionState transaction; + transaction.states.push_back({}); + + if (newParentId == UNASSIGNED_LAYER_ID) { + transaction.states.front().state.parentSurfaceControlForChild = nullptr; + } else { + transaction.states.front().state.parentSurfaceControlForChild = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + sp<LayerHandle>::make(newParentId), + static_cast<int32_t>(newParentId), "Test"); + } + transaction.states.front().state.what = layer_state_t::eReparent; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + TransactionState setLayer(uint32_t id, int32_t z) { + TransactionState transaction; + transaction.states.push_back({}); + transaction.states.front().state.z = z; + transaction.states.front().state.what = layer_state_t::eLayerChanged; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) { + TransactionState transaction; + transaction.states.push_back({}); + + if (relativeParentId == UNASSIGNED_LAYER_ID) { + transaction.states.front().state.relativeLayerSurfaceControl = nullptr; + } else { + transaction.states.front().state.relativeLayerSurfaceControl = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + sp<LayerHandle>::make(relativeParentId), + static_cast<int32_t>(relativeParentId), "Test"); + } + transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager, + uint32_t layerId) { + return lifecycleManager.getLayerFromId(layerId); + } + + std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; +}; + +TEST_F(LayerLifecycleManagerTest, addLayers) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(rootLayer(3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1, 2, 3}); + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + listener->expectLayersAdded({1, 2, 3}); + listener->expectLayersDestroyed({1, 2, 3}); +} + +TEST_F(LayerLifecycleManagerTest, updateLayerStates) { + LayerLifecycleManager lifecycleManager; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.z = 2; + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + transactions.clear(); + + auto& managedLayers = lifecycleManager.getLayers(); + ASSERT_EQ(managedLayers.size(), 1u); + + EXPECT_EQ(managedLayers.front()->z, 2); + EXPECT_TRUE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z)); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + ASSERT_EQ(managedLayers.size(), 1u); + EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z)); + + // apply transactions that do not affect the hierarchy + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.backgroundBlurRadius = 22; + transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged; + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + EXPECT_EQ(managedLayers.front()->backgroundBlurRadius, 22u); +} + +TEST_F(LayerLifecycleManagerTest, layerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1}); + lifecycleManager.commitChanges(); + + SCOPED_TRACE("layerWithoutHandleIsDestroyed"); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1}); +} + +TEST_F(LayerLifecycleManagerTest, rootLayerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); + + lifecycleManager.onHandlesDestroyed({3}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.onHandlesDestroyed({3}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.onHandlesDestroyed({3, 4}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3, 4}); +} + +TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({reparentLayer(4, 2)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)}); + EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({setLayer(4, 1)}); + EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + + auto& managedLayers = lifecycleManager.getLayers(); + ASSERT_EQ(managedLayers.size(), 2u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({}); + EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf); +} + +TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + transactions.back().states.front().state.surface = handle; + + lifecycleManager.applyTransactions(transactions); + + ASSERT_EQ(lifecycleManager.getLayers().size(), 1u); + ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 1u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({2}); +} + +TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + transactions.emplace_back(); + lifecycleManager.applyTransactions(transactions); + lifecycleManager.onHandlesDestroyed({1}); + + ASSERT_EQ(lifecycleManager.getLayers().size(), 0u); + ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1, 2}); +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp index 373fd74020..e6e02c1806 100644 --- a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp @@ -27,6 +27,8 @@ #include <gui/LayerMetadata.h> #include <log/log.h> +using android::gui::LayerMetadata; + namespace android { namespace { @@ -113,4 +115,4 @@ TEST_F(LayerMetadataTest, merge) { } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file +#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp index 4974f90383..95e54f655b 100644 --- a/services/surfaceflinger/tests/unittests/LayerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp @@ -17,7 +17,6 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include <EffectLayer.h> #include <gtest/gtest.h> #include <ui/FloatRect.h> #include <ui/Transform.h> diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp index 5a2c1479bb..ee42e19c34 100644 --- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp @@ -29,13 +29,13 @@ sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) sp<Client> client; LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, LayerMetadata()); - return new BufferStateLayer(args); + return sp<Layer>::make(args); } sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) { sp<Client> client; LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); - return new EffectLayer(args); + return sp<Layer>::make(args); } std::string PrintToStringParamName( @@ -53,13 +53,15 @@ void BaseLayerTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h index fc9b6a27aa..ab446fafeb 100644 --- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h @@ -23,8 +23,6 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#include "BufferStateLayer.h" -#include "EffectLayer.h" #include "Layer.h" // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index e0aa0b1768..5e1042e91f 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -32,8 +32,9 @@ using namespace testing; using CallbackToken = scheduler::VSyncDispatch::CallbackToken; struct NoOpCompositor final : ICompositor { - bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } - void composite(nsecs_t, int64_t) override {} + void configure() override {} + bool commit(TimePoint, VsyncId, TimePoint) override { return false; } + void composite(TimePoint, VsyncId) override {} void sample() override {} } gNoOpCompositor; @@ -41,12 +42,15 @@ class TestableMessageQueue : public impl::MessageQueue { struct MockHandler : MessageQueue::Handler { using MessageQueue::Handler::Handler; - MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override)); + MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override)); }; explicit TestableMessageQueue(sp<MockHandler> handler) : impl::MessageQueue(gNoOpCompositor, handler), mHandler(std::move(handler)) {} + // impl::MessageQueue overrides: + void onFrameSignal(ICompositor&, VsyncId, TimePoint) override {} + public: TestableMessageQueue() : TestableMessageQueue(sp<MockHandler>::make(*this)) {} @@ -71,7 +75,7 @@ struct MockTokenManager : frametimeline::TokenManager { struct MessageQueueTest : testing::Test { void SetUp() override { EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration)); + EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration)); EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); } @@ -80,16 +84,15 @@ struct MessageQueueTest : testing::Test { TestableMessageQueue mEventQueue; const CallbackToken mCallbackToken{5}; - constexpr static auto mDuration = std::chrono::nanoseconds(100ms); - constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms); + + static constexpr Duration kDuration = 100ms; + static constexpr Duration kDifferentDuration = 250ms; }; namespace { -/* ------------------------------------------------------------------------ - * Test cases - */ + TEST_F(MessageQueueTest, commit) { - const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, .earliestVsync = 0}; EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); @@ -103,7 +106,7 @@ TEST_F(MessageQueueTest, commit) { TEST_F(MessageQueueTest, commitTwice) { InSequence s; - const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, .earliestVsync = 0}; @@ -122,7 +125,7 @@ TEST_F(MessageQueueTest, commitTwice) { TEST_F(MessageQueueTest, commitTwiceWithCallback) { InSequence s; - const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, .earliestVsync = 0}; @@ -132,33 +135,36 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); - const auto startTime = 100; - const auto endTime = startTime + mDuration.count(); - const auto presentTime = 500; - const auto vsyncId = 42; + constexpr TimePoint kStartTime = TimePoint::fromNs(100); + constexpr TimePoint kEndTime = kStartTime + kDuration; + constexpr TimePoint kPresentTime = TimePoint::fromNs(500); + constexpr VsyncId vsyncId{42}; + EXPECT_CALL(mTokenManager, - generateTokenForPredictions( - frametimeline::TimelineItem(startTime, endTime, presentTime))) - .WillOnce(Return(vsyncId)); - EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1); - EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime)); + generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(), + kEndTime.ns(), + kPresentTime.ns()))) + .WillOnce(Return(vsyncId.value)); + EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1); + EXPECT_NO_FATAL_FAILURE( + mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns())); EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); const auto timingAfterCallback = - scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = presentTime}; + .earliestVsync = kPresentTime.ns()}; EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } TEST_F(MessageQueueTest, commitWithDurationChange) { - EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration)); + EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(kDifferentDuration)); const auto timing = - scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(), + scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(), .readyDuration = 0, .earliestVsync = 0}; diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 8711a42b2b..2d66d3cf92 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -39,15 +39,15 @@ class PowerAdvisorTest : public testing::Test { public: void SetUp() override; void startPowerHintSession(); - void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod); - void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod); - nsecs_t getFenceWaitDelayDuration(bool skipValidate); + void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod); + void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime); + Duration getFenceWaitDelayDuration(bool skipValidate); protected: TestableSurfaceFlinger mFlinger; std::unique_ptr<PowerAdvisor> mPowerAdvisor; NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper; - nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count(); + Duration kErrorMargin = 1ms; }; void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) { @@ -67,21 +67,21 @@ void PowerAdvisorTest::startPowerHintSession() { mPowerAdvisor->startPowerHintSession(threadIds); } -void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) { - mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget); +void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration, + TimePoint expectedPresentTime) { + mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTargetDuration); mPowerAdvisor->setExpectedPresentTime(expectedPresentTime); } -void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) { +void PowerAdvisorTest::fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod) { mPowerAdvisor->setCommitStart(startTime); - mPowerAdvisor->setFrameDelay(0); + mPowerAdvisor->setFrameDelay(0ns); mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); } -nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) { +Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) { return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate - : PowerAdvisor::kFenceWaitStartDelayValidated) - .count(); + : PowerAdvisor::kFenceWaitStartDelayValidated); } namespace { @@ -93,11 +93,11 @@ TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) { std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)}; // 60hz - const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; - const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count(); - const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); + const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60}; + const Duration presentDuration = 5ms; + const Duration postCompDuration = 1ms; - nsecs_t startTime = 100; + TimePoint startTime{100ns}; // advisor only starts on frame 2 so do an initial no-op frame fakeBasicFrameTiming(startTime, vsyncPeriod); @@ -109,14 +109,14 @@ TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) { // increment the frame startTime += vsyncPeriod; - const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration; + const Duration expectedDuration = kErrorMargin + presentDuration + postCompDuration; EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); fakeBasicFrameTiming(startTime, vsyncPeriod); setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); mPowerAdvisor->setDisplays(displayIds); - mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); - mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000); + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us); mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); mPowerAdvisor->sendActualWorkDuration(); } @@ -128,12 +128,12 @@ TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) { std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)}; // 60hz - const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; - const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count(); - const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); - const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count(); + const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60}; + const Duration presentDuration = 5ms; + const Duration postCompDuration = 1ms; + const Duration hwcBlockedDuration = 500us; - nsecs_t startTime = 100; + TimePoint startTime{100ns}; // advisor only starts on frame 2 so do an initial no-op frame fakeBasicFrameTiming(startTime, vsyncPeriod); @@ -145,17 +145,17 @@ TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) { // increment the frame startTime += vsyncPeriod; - const nsecs_t expectedDuration = kErrorMargin + presentDuration + + const Duration expectedDuration = kErrorMargin + presentDuration + getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration; EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); fakeBasicFrameTiming(startTime, vsyncPeriod); setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); mPowerAdvisor->setDisplays(displayIds); - mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); - mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000); + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 3ms); // now report the fence as having fired during the display HWC time - mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration, + mPowerAdvisor->setSfPresentTiming(startTime + 2ms + hwcBlockedDuration, startTime + presentDuration); mPowerAdvisor->sendActualWorkDuration(); } @@ -168,12 +168,12 @@ TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) { GpuVirtualDisplayId(1)}; // 60hz - const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; + const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60}; // make present duration much later than the hwc display by itself will account for - const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count(); - const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); + const Duration presentDuration{10ms}; + const Duration postCompDuration{1ms}; - nsecs_t startTime = 100; + TimePoint startTime{100ns}; // advisor only starts on frame 2 so do an initial no-op frame fakeBasicFrameTiming(startTime, vsyncPeriod); @@ -185,7 +185,7 @@ TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) { // increment the frame startTime += vsyncPeriod; - const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration; + const Duration expectedDuration = kErrorMargin + presentDuration + postCompDuration; EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); fakeBasicFrameTiming(startTime, vsyncPeriod); @@ -193,8 +193,8 @@ TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) { mPowerAdvisor->setDisplays(displayIds); // don't report timing for the gpu displays since they don't use hwc - mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); - mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000); + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us); mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); mPowerAdvisor->sendActualWorkDuration(); } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp deleted file mode 100644 index 188fd58dea..0000000000 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ /dev/null @@ -1,2077 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "SchedulerUnittests" - -#include <ftl/enum.h> -#include <gmock/gmock.h> -#include <log/log.h> -#include <ui/Size.h> - -#include "DisplayHardware/HWC2.h" -#include "FpsOps.h" -#include "Scheduler/RefreshRateConfigs.h" -#include "mock/DisplayHardware/MockDisplayMode.h" - -using namespace std::chrono_literals; - -namespace android::scheduler { - -namespace hal = android::hardware::graphics::composer::hal; - -using LayerVoteType = RefreshRateConfigs::LayerVoteType; -using LayerRequirement = RefreshRateConfigs::LayerRequirement; - -using mock::createDisplayMode; - -struct TestableRefreshRateConfigs : RefreshRateConfigs { - using RefreshRateConfigs::RefreshRateConfigs; - - DisplayModePtr getMinSupportedRefreshRate() const { - std::lock_guard lock(mLock); - return mMinRefreshRateModeIt->second; - } - - DisplayModePtr getMaxSupportedRefreshRate() const { - std::lock_guard lock(mLock); - return mMaxRefreshRateModeIt->second; - } - - DisplayModePtr getMinRefreshRateByPolicy() const { - std::lock_guard lock(mLock); - return getMinRefreshRateByPolicyLocked(); - } - - const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; } - - using RefreshRateConfigs::GetBestRefreshRateCache; - auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; } - - auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const { - return RefreshRateConfigs::getBestRefreshRate(layers, signals); - } - - DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {}, - GlobalSignals signals = {}) const { - return getBestRefreshRateAndSignals(layers, signals).first; - } -}; - -class RefreshRateConfigsTest : public testing::Test { -protected: - RefreshRateConfigsTest(); - ~RefreshRateConfigsTest(); - - static constexpr DisplayModeId kModeId60{0}; - static constexpr DisplayModeId kModeId90{1}; - static constexpr DisplayModeId kModeId72{2}; - static constexpr DisplayModeId kModeId120{3}; - static constexpr DisplayModeId kModeId30{4}; - static constexpr DisplayModeId kModeId25{5}; - static constexpr DisplayModeId kModeId50{6}; - static constexpr DisplayModeId kModeId24{7}; - static constexpr DisplayModeId kModeId24Frac{8}; - static constexpr DisplayModeId kModeId30Frac{9}; - static constexpr DisplayModeId kModeId60Frac{10}; - - static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz); - static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz); - static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz); - static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1); - static inline const DisplayModePtr kMode90_4K = - createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}); - static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz); - static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1); - static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz); - static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1); - static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz); - static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1); - static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz); - static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz); - static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1); - static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz); - static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz); - static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz); - - // Test configurations. - static inline const DisplayModes kModes_60 = makeModes(kMode60); - static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90); - static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1); - static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K); - static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72); - static inline const DisplayModes kModes_60_90_72_120 = - makeModes(kMode60, kMode90, kMode72, kMode120); - static inline const DisplayModes kModes_30_60_72_90_120 = - makeModes(kMode60, kMode90, kMode72, kMode120, kMode30); - - static inline const DisplayModes kModes_30_60 = - makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30); - static inline const DisplayModes kModes_30_60_72_90 = - makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30); - static inline const DisplayModes kModes_30_60_90 = - makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30); - static inline const DisplayModes kModes_25_30_50_60 = - makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50); - static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120); - - // This is a typical TV configuration. - static inline const DisplayModes kModes_24_25_30_50_60_Frac = - makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60, - kMode60Frac); -}; - -RefreshRateConfigsTest::RefreshRateConfigsTest() { - 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()); -} - -RefreshRateConfigsTest::~RefreshRateConfigsTest() { - 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()); -} - -namespace { - -TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) { - RefreshRateConfigs configs(kModes_60, kModeId60); - EXPECT_FALSE(configs.canSwitch()); -} - -TEST_F(RefreshRateConfigsTest, invalidPolicy) { - RefreshRateConfigs configs(kModes_60, kModeId60); - EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0); - EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0); -} - -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - const auto minRate = configs.getMinSupportedRefreshRate(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); - - EXPECT_EQ(kMode60, minRate); - EXPECT_EQ(kMode90, performanceRate); - - const auto minRateByPolicy = configs.getMinRefreshRateByPolicy(); - const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(minRateByPolicy, minRate); - EXPECT_EQ(performanceRateByPolicy, performanceRate); -} - -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode60, minRate); - EXPECT_EQ(kMode60, minRate60); - EXPECT_EQ(kMode60, performanceRate60); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0); - configs.setActiveModeId(kModeId90); - - const auto minRate90 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode90_G1, performanceRate); - EXPECT_EQ(kMode90_G1, minRate90); - EXPECT_EQ(kMode90_G1, performanceRate90); -} - -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) { - TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60); - - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode60, minRate); - EXPECT_EQ(kMode60, minRate60); - EXPECT_EQ(kMode60, performanceRate60); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0); - configs.setActiveModeId(kModeId90); - - const auto minRate90 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode90_4K, performanceRate); - EXPECT_EQ(kMode90_4K, minRate90); - EXPECT_EQ(kMode90_4K, performanceRate90); -} - -TEST_F(RefreshRateConfigsTest, twoModes_policyChange) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode60, minRate); - EXPECT_EQ(kMode90, performanceRate); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); - - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); - - EXPECT_EQ(kMode60, minRate60); - EXPECT_EQ(kMode60, performanceRate60); -} - -TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - { - const auto mode = configs.getActiveMode(); - EXPECT_EQ(mode->getId(), kModeId60); - } - - configs.setActiveModeId(kModeId90); - { - const auto mode = configs.getActiveMode(); - EXPECT_EQ(mode->getId(), kModeId90); - } - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); - { - const auto mode = configs.getActiveMode(); - EXPECT_EQ(mode->getId(), kModeId90); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) { - { - TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72); - - // If there are no layers we select the default frame rate, which is the max of the primary - // range. - EXPECT_EQ(kMode90, configs.getBestRefreshRate()); - - EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR); - EXPECT_EQ(kMode60, configs.getBestRefreshRate()); - } - { - // We select max even when this will cause a non-seamless switch. - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - constexpr bool kAllowGroupSwitching = true; - EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}), - NO_ERROR); - EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate()); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::Min; - lr.name = "Min"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.name = ""; - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); - - lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); - - lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0); - lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::Min; - lr.name = "Min"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) { - TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 48_Hz; - lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 48_Hz; - lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::Heuristic; - lr1.name = "24Hz Heuristic"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, - {.frameRateMultipleThreshold = 120}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - auto& lr3 = layers[2]; - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::Heuristic; - lr1.name = "24Hz Heuristic"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.name = "24Hz ExplicitDefault"; - lr2.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Max; - lr2.name = "Max"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 120_Hz; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.name = "120Hz ExplicitDefault"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 24_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "24Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 120_Hz; - lr2.vote = LayerVoteType::ExplicitExact; - lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 10_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "30Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 120_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - lr1.desiredRefreshRate = 30_Hz; - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.name = "30Hz ExplicitExactOrMultiple"; - lr2.desiredRefreshRate = 30_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.name = "30Hz ExplicitExactOrMultiple"; - lr3.vote = LayerVoteType::Heuristic; - lr3.desiredRefreshRate = 120_Hz; - lr3.name = "120Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) { - TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::Min; - lr.name = "Min"; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 90_Hz; - lr.vote = LayerVoteType::Heuristic; - lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr.desiredRefreshRate = 45_Hz; - lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr.desiredRefreshRate = 30_Hz; - lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr.desiredRefreshRate = 24_Hz; - lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr.desiredRefreshRate = 24_Hz; - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - lr.name = "24Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) { - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.vote = LayerVoteType::Min; - lr2.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Min; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Min; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Max; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Max; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Heuristic; - lr1.desiredRefreshRate = 15_Hz; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Heuristic; - lr1.desiredRefreshRate = 30_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { - lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers); - EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " - << to_string(mode->getFps()); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) { - TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, - {.frameRateMultipleThreshold = 120}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { - lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers); - EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " - << to_string(mode->getFps()); - } -} - -TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.vote = LayerVoteType::Heuristic; - lr1.desiredRefreshRate = 60_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::Heuristic; - lr1.desiredRefreshRate = 90_Hz; - lr2.vote = LayerVoteType::ExplicitExactOrMultiple; - lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) { - lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers, {}); - EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses " - << to_string(mode->getFps()); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 90_Hz; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::ExplicitDefault; - lr2.desiredRefreshRate = 90_Hz; - lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Max; - lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 30_Hz; - lr1.name = "30Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 90_Hz; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 30_Hz; - lr1.name = "30Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Max; - lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::NoVote; - lr2.name = "NoVote"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::NoVote; - lr2.name = "NoVote"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Max; - lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Max; - lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - // The other layer starts to provide buffers - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 90_Hz; - lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, touchConsidered) { - RefreshRateConfigs configs(kModes_60_90, kModeId60); - - auto [_, signals] = configs.getBestRefreshRate({}, {}); - EXPECT_FALSE(signals.touch); - - std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true}); - EXPECT_TRUE(signals.touch); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; - auto& lr1 = layers[0]; - auto& lr2 = layers[1]; - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); - EXPECT_TRUE(signals.touch); - - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); - EXPECT_FALSE(signals.touch); - - lr1.vote = LayerVoteType::ExplicitExactOrMultiple; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); - EXPECT_TRUE(signals.touch); - - lr1.vote = LayerVoteType::ExplicitDefault; - lr1.desiredRefreshRate = 60_Hz; - lr1.name = "60Hz ExplicitExactOrMultiple"; - lr2.vote = LayerVoteType::Heuristic; - lr2.desiredRefreshRate = 60_Hz; - lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); - EXPECT_FALSE(signals.touch); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { - TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - // Prepare a table with the vote and the expected refresh rate - const std::initializer_list<std::pair<Fps, Fps>> testCases = { - {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz}, - - {100_Hz, 90_Hz}, {90_Hz, 90_Hz}, {89_Hz, 90_Hz}, - - {80_Hz, 72_Hz}, {73_Hz, 72_Hz}, {72_Hz, 72_Hz}, {71_Hz, 72_Hz}, {70_Hz, 72_Hz}, - - {65_Hz, 60_Hz}, {60_Hz, 60_Hz}, {59_Hz, 60_Hz}, {58_Hz, 60_Hz}, - - {55_Hz, 90_Hz}, {50_Hz, 90_Hz}, {45_Hz, 90_Hz}, - - {42_Hz, 120_Hz}, {40_Hz, 120_Hz}, {39_Hz, 120_Hz}, - - {37_Hz, 72_Hz}, {36_Hz, 72_Hz}, {35_Hz, 72_Hz}, - - {30_Hz, 60_Hz}, - }; - - for (auto [desired, expected] : testCases) { - lr.vote = LayerVoteType::ExplicitDefault; - lr.desiredRefreshRate = desired; - - std::stringstream ss; - ss << "ExplicitDefault " << desired; - lr.name = ss.str(); - - EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps()); - } -} - -TEST_F(RefreshRateConfigsTest, - getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) { - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - // Test that 23.976 will choose 24 if 23.976 is not supported - { - TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); - - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - lr.desiredRefreshRate = 23.976_Hz; - lr.name = "ExplicitExactOrMultiple 23.976 Hz"; - EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId()); - } - - // Test that 24 will choose 23.976 if 24 is not supported - { - TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); - - lr.desiredRefreshRate = 24_Hz; - lr.name = "ExplicitExactOrMultiple 24 Hz"; - EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId()); - } - - // Test that 29.97 will prefer 59.94 over 60 and 30 - { - TestableRefreshRateConfigs configs(makeModes(kMode24, kMode24Frac, kMode25, kMode30, - kMode60, kMode60Frac), - kModeId60); - - lr.desiredRefreshRate = 29.97_Hz; - lr.name = "ExplicitExactOrMultiple 29.97 Hz"; - EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId()); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) { - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - // Test that voting for supported refresh rate will select this refresh rate - { - TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60); - - for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) { - lr.vote = LayerVoteType::ExplicitExact; - lr.desiredRefreshRate = desired; - std::stringstream ss; - ss << "ExplicitExact " << desired; - lr.name = ss.str(); - - EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps()); - } - } - - // Test that 23.976 will choose 24 if 23.976 is not supported - { - TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); - - lr.vote = LayerVoteType::ExplicitExact; - lr.desiredRefreshRate = 23.976_Hz; - lr.name = "ExplicitExact 23.976 Hz"; - EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId()); - } - - // Test that 24 will choose 23.976 if 24 is not supported - { - TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); - - lr.desiredRefreshRate = 24_Hz; - lr.name = "ExplicitExact 24 Hz"; - EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId()); - } -} - -TEST_F(RefreshRateConfigsTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { - RefreshRateConfigs configs(kModes_60_90, kModeId90); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitDefault; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz ExplicitDefault"; - lr.focused = true; - - const auto [mode, signals] = configs.getBestRefreshRate(layers, {.touch = true, .idle = true}); - - EXPECT_EQ(mode, kMode60); - EXPECT_FALSE(signals.touch); -} - -TEST_F(RefreshRateConfigsTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitDefault; - lr.desiredRefreshRate = 90_Hz; - lr.name = "90Hz ExplicitDefault"; - lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true})); -} - -TEST_F(RefreshRateConfigsTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId90); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); - - const auto [mode, signals] = configs.getBestRefreshRateAndSignals({}, {}); - EXPECT_EQ(mode, kMode90); - EXPECT_FALSE(signals.touch); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& lr = layers[0]; - - lr.vote = LayerVoteType::ExplicitExactOrMultiple; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz ExplicitExactOrMultiple"; - lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::ExplicitDefault; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz ExplicitDefault"; - lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.focused = true; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Heuristic; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Heuristic"; - lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Max; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Max"; - lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.vote = LayerVoteType::Min; - lr.desiredRefreshRate = 60_Hz; - lr.name = "60Hz Min"; - lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - // The default policy doesn't allow group switching. Verify that no - // group switches are performed. - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitDefault; - layer.desiredRefreshRate = 90_Hz; - layer.seamlessness = Seamlessness::SeamedAndSeamless; - layer.name = "90Hz ExplicitDefault"; - layer.focused = true; - - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitDefault; - layer.desiredRefreshRate = 90_Hz; - layer.seamlessness = Seamlessness::SeamedAndSeamless; - layer.name = "90Hz ExplicitDefault"; - layer.focused = true; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - // Verify that we won't change the group if seamless switch is required. - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitDefault; - layer.desiredRefreshRate = 90_Hz; - layer.seamlessness = Seamlessness::OnlySeamless; - layer.name = "90Hz ExplicitDefault"; - layer.focused = true; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - configs.setActiveModeId(kModeId90); - - // Verify that we won't do a seamless switch if we request the same mode as the default - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitDefault; - layer.desiredRefreshRate = 60_Hz; - layer.seamlessness = Seamlessness::OnlySeamless; - layer.name = "60Hz ExplicitDefault"; - layer.focused = true; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - configs.setActiveModeId(kModeId90); - - // Verify that if the current config is in another group and there are no layers with - // seamlessness=SeamedAndSeamless we'll go back to the default group. - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitDefault; - layer.desiredRefreshRate = 60_Hz; - layer.seamlessness = Seamlessness::Default; - layer.name = "60Hz ExplicitDefault"; - layer.focused = true; - - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - configs.setActiveModeId(kModeId90); - - // If there's a layer with seamlessness=SeamedAndSeamless, another layer with - // seamlessness=OnlySeamless can't change the mode group. - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].vote = LayerVoteType::ExplicitDefault; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].seamlessness = Seamlessness::OnlySeamless; - layers[0].name = "60Hz ExplicitDefault"; - layers[0].focused = true; - - layers.push_back(LayerRequirement{.weight = 0.5f}); - layers[1].vote = LayerVoteType::ExplicitDefault; - layers[1].seamlessness = Seamlessness::SeamedAndSeamless; - layers[1].desiredRefreshRate = 90_Hz; - layers[1].name = "90Hz ExplicitDefault"; - layers[1].focused = false; - - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - configs.setActiveModeId(kModeId90); - - // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with - // seamlessness=Default can't change the mode group back to the group of the default - // mode. - // For example, this may happen when a video playback requests and gets a seamed switch, - // but another layer (with default seamlessness) starts animating. The animating layer - // should not cause a seamed switch. - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].seamlessness = Seamlessness::Default; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].focused = true; - layers[0].vote = LayerVoteType::ExplicitDefault; - layers[0].name = "60Hz ExplicitDefault"; - - layers.push_back(LayerRequirement{.weight = 0.1f}); - layers[1].seamlessness = Seamlessness::SeamedAndSeamless; - layers[1].desiredRefreshRate = 90_Hz; - layers[1].focused = true; - layers[1].vote = LayerVoteType::ExplicitDefault; - layers[1].name = "90Hz ExplicitDefault"; - - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - configs.setActiveModeId(kModeId90); - - // Layer with seamlessness=Default can change the mode group if there's a not - // focused layer with seamlessness=SeamedAndSeamless. This happens for example, - // when in split screen mode the user switches between the two visible applications. - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].seamlessness = Seamlessness::Default; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].focused = true; - layers[0].vote = LayerVoteType::ExplicitDefault; - layers[0].name = "60Hz ExplicitDefault"; - - layers.push_back(LayerRequirement{.weight = 0.7f}); - layers[1].seamlessness = Seamlessness::SeamedAndSeamless; - layers[1].desiredRefreshRate = 90_Hz; - layers[1].focused = false; - layers[1].vote = LayerVoteType::ExplicitDefault; - layers[1].name = "90Hz ExplicitDefault"; - - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { - TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); - - // Allow group switching. - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::ExplicitExactOrMultiple; - layer.desiredRefreshRate = 60_Hz; - layer.seamlessness = Seamlessness::SeamedAndSeamless; - layer.name = "60Hz ExplicitExactOrMultiple"; - layer.focused = true; - - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); - - configs.setActiveModeId(kModeId120); - EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { - TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60); - - // Allow group switching. - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault", - .vote = LayerVoteType::ExplicitDefault, - .desiredRefreshRate = 60_Hz, - .seamlessness = Seamlessness::SeamedAndSeamless, - .weight = 0.5f, - .focused = false}, - {.name = "25Hz ExplicitExactOrMultiple", - .vote = LayerVoteType::ExplicitExactOrMultiple, - .desiredRefreshRate = 25_Hz, - .seamlessness = Seamlessness::OnlySeamless, - .weight = 1.f, - .focused = true}}; - - EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId()); - - auto& seamedLayer = layers[0]; - seamedLayer.desiredRefreshRate = 30_Hz; - seamedLayer.name = "30Hz ExplicitDefault"; - configs.setActiveModeId(kModeId30); - - EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90); - - // Allow group switching. - RefreshRateConfigs::Policy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; - policy.allowGroupSwitching = true; - EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - - std::vector<LayerRequirement> layers = { - {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}}; - - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); -} - -TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].name = "Test layer"; - - struct Args { - bool touch = false; - bool focused = true; - }; - - // Return the config ID from calling getBestRefreshRate() for a single layer with the - // given voteType and fps. - auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId { - layers[0].vote = voteType; - layers[0].desiredRefreshRate = fps; - layers[0].focused = args.focused; - return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId(); - }; - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0); - - EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId()); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); - EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); - EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); - - // Unfocused layers are not allowed to override primary config. - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false})); - EXPECT_EQ(kModeId60, - getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false})); - - // Touch boost should be restricted to the primary range. - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true})); - - // When we're higher than the primary range max due to a layer frame rate setting, touch boost - // shouldn't drag us back down to the primary range max. - EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true})); - EXPECT_EQ(kModeId60, - getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true})); - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0); - - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); - EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); -} - -TEST_F(RefreshRateConfigsTest, idle) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].name = "Test layer"; - - const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId { - layers[0].vote = voteType; - layers[0].desiredRefreshRate = 90_Hz; - - const auto [refreshRate, signals] = - configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true}); - - // Refresh rate will be chosen by either touch state or idle state. - EXPECT_EQ(!touchActive, signals.idle); - return refreshRate->getId(); - }; - - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); - - // Idle should be lower priority than touch boost. - { - constexpr bool kTouchActive = true; - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); - EXPECT_EQ(kModeId90, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); - } - - // With no layers, idle should still be lower priority than touch boost. - EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId()); - - // Idle should be higher precedence than other layer frame rate considerations. - configs.setActiveModeId(kModeId90); - - { - constexpr bool kTouchActive = false; - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); - EXPECT_EQ(kModeId60, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); - } - - // Idle should be applied rather than the current config when there are no layers. - EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId()); -} - -TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { - const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps)); - const Fps expectedFrameRate = [fps] { - if (fps < 26.91f) return 24_Hz; - if (fps < 37.51f) return 30_Hz; - if (fps < 52.51f) return 45_Hz; - if (fps < 66.01f) return 60_Hz; - if (fps < 81.01f) return 72_Hz; - return 90_Hz; - }(); - - EXPECT_EQ(expectedFrameRate, knownFrameRate); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - - struct Expectation { - Fps fps; - DisplayModePtr mode; - }; - - const std::initializer_list<Expectation> knownFrameRatesExpectations = { - {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90}, - {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90}, - }; - - // Make sure the test tests all the known frame rate - const auto& knownFrameRates = configs.knownFrameRates(); - const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(), - knownFrameRatesExpectations.begin(), - [](Fps fps, const Expectation& expected) { - return isApproxEqual(fps, expected.fps); - }); - EXPECT_TRUE(equal); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - auto& layer = layers[0]; - layer.vote = LayerVoteType::Heuristic; - - for (const auto& [fps, mode] : knownFrameRatesExpectations) { - layer.desiredRefreshRate = fps; - EXPECT_EQ(mode, configs.getBestRefreshRate(layers)); - } -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - auto& explicitExactLayer = layers[0]; - auto& explicitExactOrMultipleLayer = layers[1]; - - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; - explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; - explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - - explicitExactLayer.vote = LayerVoteType::ExplicitExact; - explicitExactLayer.name = "ExplicitExact"; - explicitExactLayer.desiredRefreshRate = 30_Hz; - - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true})); - - explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; - explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, - {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - auto& explicitExactLayer = layers[0]; - auto& explicitExactOrMultipleLayer = layers[1]; - - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; - explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; - explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - - explicitExactLayer.vote = LayerVoteType::ExplicitExact; - explicitExactLayer.name = "ExplicitExact"; - explicitExactLayer.desiredRefreshRate = 30_Hz; - - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true})); - - explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; - explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - - using GlobalSignals = RefreshRateConfigs::GlobalSignals; - const auto args = std::make_pair(std::vector<LayerRequirement>{}, - GlobalSignals{.touch = true, .idle = true}); - const auto result = std::make_pair(kMode90, GlobalSignals{.touch = true}); - - configs.mutableGetBestRefreshRateCache() = {args, result}; - - EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second)); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - - EXPECT_FALSE(configs.mutableGetBestRefreshRateCache()); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true}; - - const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals); - - const auto& cache = configs.mutableGetBestRefreshRateCache(); - ASSERT_TRUE(cache); - - EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals)); - EXPECT_EQ(cache->result, result); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { - TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - auto& explicitExactLayer = layers[0]; - auto& explicitExactOrMultipleLayer = layers[1]; - - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; - explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; - explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - - explicitExactLayer.vote = LayerVoteType::ExplicitExact; - explicitExactLayer.name = "ExplicitExact"; - explicitExactLayer.desiredRefreshRate = 30_Hz; - - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true})); - - explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote; - - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true})); -} - -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { - TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60, - {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}}; - auto& explicitDefaultLayer = layers[0]; - auto& explicitExactOrMultipleLayer = layers[1]; - - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; - explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; - explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - - explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault; - explicitDefaultLayer.name = "ExplicitDefault"; - explicitDefaultLayer.desiredRefreshRate = 59.94_Hz; - - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); -} - -// b/190578904 -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) { - constexpr int kMinRefreshRate = 10; - constexpr int kMaxRefreshRate = 240; - - DisplayModes displayModes; - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const DisplayModeId modeId(fps); - displayModes.try_emplace(modeId, - createDisplayMode(modeId, - Fps::fromValue(static_cast<float>(fps)))); - } - - const TestableRefreshRateConfigs configs(std::move(displayModes), - DisplayModeId(kMinRefreshRate)); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { - layers[0].desiredRefreshRate = fps; - layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers)->getFps().getIntValue()) - << "Failed for " << ftl::enum_string(vote); - }; - - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); - testRefreshRate(refreshRate, LayerVoteType::Heuristic); - testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault); - testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple); - testRefreshRate(refreshRate, LayerVoteType::ExplicitExact); - } -} - -// b/190578904 -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) { - constexpr DisplayModeId kActiveModeId{0}; - DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz), - createDisplayMode(DisplayModeId(1), 53_Hz), - createDisplayMode(DisplayModeId(2), 55_Hz), - createDisplayMode(DisplayModeId(3), 60_Hz)); - - const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; - const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId); - - const std::vector<LayerRequirement> layers = { - { - .vote = LayerVoteType::ExplicitDefault, - .desiredRefreshRate = 43_Hz, - .seamlessness = Seamlessness::SeamedAndSeamless, - .weight = 0.41f, - }, - { - .vote = LayerVoteType::ExplicitExactOrMultiple, - .desiredRefreshRate = 53_Hz, - .seamlessness = Seamlessness::SeamedAndSeamless, - .weight = 0.41f, - }, - }; - - EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps()); -} - -TEST_F(RefreshRateConfigsTest, modeComparison) { - EXPECT_LT(kMode60->getFps(), kMode90->getFps()); - EXPECT_GE(kMode60->getFps(), kMode60->getFps()); - EXPECT_GE(kMode90->getFps(), kMode90->getFps()); -} - -TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) { - using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; - - RefreshRateConfigs configs(kModes_60_90, kModeId90); - - // SetPolicy(60, 90), current 90Hz => TurnOn. - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); - - // SetPolicy(60, 90), current 60Hz => TurnOn. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); - - // SetPolicy(60, 60), current 60Hz => TurnOff - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); - - // SetPolicy(90, 90), current 90Hz => TurnOff. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); -} - -TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) { - using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; - - RefreshRateConfigs configs(kModes_60_120, kModeId120); - - // SetPolicy(0, 60), current 60Hz => TurnOn. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); - - // SetPolicy(60, 60), current 60Hz => TurnOff. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); - - // SetPolicy(60, 120), current 60Hz => TurnOn. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); - - // SetPolicy(120, 120), current 120Hz => TurnOff. - EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); -} - -TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30); - - const auto frameRate = 30_Hz; - Fps displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); - - configs.setActiveModeId(kModeId60); - displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); - - configs.setActiveModeId(kModeId72); - displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); - - configs.setActiveModeId(kModeId90); - displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); - - configs.setActiveModeId(kModeId120); - displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); - - configs.setActiveModeId(kModeId90); - displayRefreshRate = configs.getActiveMode()->getFps(); - EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz)); - - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz)); -} - -TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) { - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz)); - - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz)); - - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz)); - - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz)); - - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz)); - - const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}; - for (auto refreshRate : refreshRates) { - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate)); - } - - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz)); - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz)); - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz)); -} - -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120); - - EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty()); -} - -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].name = "Test layer"; - layers[0].ownerUid = 1234; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].vote = LayerVoteType::ExplicitDefault; - - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Min; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Max; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Heuristic; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); -} - -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, - {.ownerUid = 5678, .weight = 1.f}}; - - layers[0].name = "Test layer 1234"; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].vote = LayerVoteType::ExplicitDefault; - - layers[1].name = "Test layer 5678"; - layers[1].desiredRefreshRate = 30_Hz; - layers[1].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - - EXPECT_EQ(2u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - ASSERT_EQ(1u, frameRateOverrides.count(5678)); - EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); - - layers[1].vote = LayerVoteType::Heuristic; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - layers[1].ownerUid = 1234; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); -} - -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); - - std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}}; - layers[0].name = "Test layer"; - layers[0].desiredRefreshRate = 60_Hz; - layers[0].vote = LayerVoteType::ExplicitDefault; - - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - layers[0].vote = LayerVoteType::ExplicitExact; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - EXPECT_TRUE(frameRateOverrides.empty()); -} - -} // namespace -} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp new file mode 100644 index 0000000000..a3b3c4cf58 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -0,0 +1,2960 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "SchedulerUnittests" + +#include <algorithm> +#include <array> + +#include <ftl/enum.h> +#include <ftl/fake_guard.h> +#include <gmock/gmock.h> +#include <log/log.h> +#include <ui/Size.h> + +#include <scheduler/FrameRateMode.h> +#include "DisplayHardware/HWC2.h" +#include "FpsOps.h" +#include "Scheduler/RefreshRateSelector.h" +#include "mock/DisplayHardware/MockDisplayMode.h" +#include "mock/MockFrameRateMode.h" + +#include "libsurfaceflinger_unittest_main.h" + +using namespace std::chrono_literals; + +namespace android::scheduler { + +namespace hal = android::hardware::graphics::composer::hal; + +using Config = RefreshRateSelector::Config; +using LayerRequirement = RefreshRateSelector::LayerRequirement; +using LayerVoteType = RefreshRateSelector::LayerVoteType; +using SetPolicyResult = RefreshRateSelector::SetPolicyResult; + +using mock::createDisplayMode; + +struct TestableRefreshRateSelector : RefreshRateSelector { + using RefreshRateSelector::FrameRateRanking; + using RefreshRateSelector::RefreshRateOrder; + + using RefreshRateSelector::RefreshRateSelector; + + void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) { + ftl::FakeGuard guard(kMainThreadContext); + return RefreshRateSelector::setActiveMode(modeId, renderFrameRate); + } + + const DisplayMode& getActiveMode() const { + std::lock_guard lock(mLock); + return *RefreshRateSelector::getActiveModeLocked().modePtr; + } + + ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const { + std::lock_guard lock(mLock); + return ftl::as_non_null(mMinRefreshRateModeIt->second); + } + + ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const { + std::lock_guard lock(mLock); + return ftl::as_non_null(mMaxRefreshRateModeIt->second); + } + + ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const { + std::lock_guard lock(mLock); + return ftl::as_non_null(getMinRefreshRateByPolicyLocked()); + } + + ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const { + std::lock_guard lock(mLock); + return ftl::as_non_null( + getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup())); + } + + FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, + RefreshRateOrder refreshRateOrder) const { + std::lock_guard lock(mLock); + return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder); + } + + const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; } + + using RefreshRateSelector::GetRankedFrameRatesCache; + auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; } + + auto getRankedFrameRates(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const { + const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals); + + EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), + ScoredFrameRate::DescendingScore{})); + + return result; + } + + auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const { + const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals); + return std::make_pair(ranking, consideredSignals); + } + + ftl::NonNull<DisplayModePtr> getBestFrameRateMode( + const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const { + return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr; + } + + ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {}, + GlobalSignals signals = {}) const { + return getRankedFrameRates(layers, signals).ranking.front(); + } + + SetPolicyResult setPolicy(const PolicyVariant& policy) { + ftl::FakeGuard guard(kMainThreadContext); + return RefreshRateSelector::setPolicy(policy); + } + + SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) { + return setPolicy(policy); + } + + const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; } +}; + +class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> { +protected: + using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder; + + RefreshRateSelectorTest(); + ~RefreshRateSelectorTest(); + + static constexpr DisplayModeId kModeId60{0}; + static constexpr DisplayModeId kModeId90{1}; + static constexpr DisplayModeId kModeId72{2}; + static constexpr DisplayModeId kModeId120{3}; + static constexpr DisplayModeId kModeId30{4}; + static constexpr DisplayModeId kModeId25{5}; + static constexpr DisplayModeId kModeId50{6}; + static constexpr DisplayModeId kModeId24{7}; + static constexpr DisplayModeId kModeId24Frac{8}; + static constexpr DisplayModeId kModeId30Frac{9}; + static constexpr DisplayModeId kModeId60Frac{10}; + static constexpr DisplayModeId kModeId35{11}; + + static inline const ftl::NonNull<DisplayModePtr> kMode60 = + ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode60Frac = + ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode90_4K = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160})); + static inline const ftl::NonNull<DisplayModePtr> kMode72 = + ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 = + ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode120 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode30 = + ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 = + ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode30Frac = + ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode25 = + ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 = + ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode35 = + ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode50 = + ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode24 = + ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode24Frac = + ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz)); + + // Test configurations. + static inline const DisplayModes kModes_60 = makeModes(kMode60); + static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90); + static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90); + static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1); + static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K); + static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72); + static inline const DisplayModes kModes_60_90_72_120 = + makeModes(kMode60, kMode90, kMode72, kMode120); + static inline const DisplayModes kModes_30_60_72_90_120 = + makeModes(kMode60, kMode90, kMode72, kMode120, kMode30); + + static inline const DisplayModes kModes_30_60 = + makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30); + static inline const DisplayModes kModes_30_60_72_90 = + makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30); + static inline const DisplayModes kModes_30_60_90 = + makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30); + static inline const DisplayModes kModes_25_30_50_60 = + makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50); + static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120); + + // This is a typical TV configuration. + static inline const DisplayModes kModes_24_25_30_50_60_Frac = + makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60, + kMode60Frac); + + static TestableRefreshRateSelector createSelector(DisplayModes modes, + DisplayModeId activeModeId, + Config config = {}) { + config.enableFrameRateOverride = GetParam(); + return TestableRefreshRateSelector(modes, activeModeId, config); + } +}; + +RefreshRateSelectorTest::RefreshRateSelectorTest() { + 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()); +} + +RefreshRateSelectorTest::~RefreshRateSelectorTest() { + 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()); +} + +namespace { + +INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest, + testing::Values(Config::FrameRateOverride::Disabled, + Config::FrameRateOverride::AppOverrideNativeRefreshRates, + Config::FrameRateOverride::AppOverride, + Config::FrameRateOverride::Enabled)); + +TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) { + auto selector = createSelector(kModes_60, kModeId60); + if (GetParam() == Config::FrameRateOverride::Enabled) { + EXPECT_TRUE(selector.canSwitch()); + } else { + EXPECT_FALSE(selector.canSwitch()); + } +} + +TEST_P(RefreshRateSelectorTest, invalidPolicy) { + auto selector = createSelector(kModes_60, kModeId60); + + EXPECT_EQ(SetPolicyResult::Invalid, + selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}})); + EXPECT_EQ(SetPolicyResult::Invalid, + selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}})); +} + +TEST_P(RefreshRateSelectorTest, unchangedPolicy) { + auto selector = createSelector(kModes_60_90, kModeId60); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + + EXPECT_EQ(SetPolicyResult::Unchanged, + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + + // Override to the same policy. + EXPECT_EQ(SetPolicyResult::Unchanged, + selector.setPolicy(RefreshRateSelector::OverridePolicy{kModeId90, {60_Hz, 90_Hz}})); + + // Clear override to restore DisplayManagerPolicy. + EXPECT_EQ(SetPolicyResult::Unchanged, + selector.setPolicy(RefreshRateSelector::NoOverridePolicy{})); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}})); +} + +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) { + auto selector = createSelector(kModes_60_90, kModeId60); + + const auto minRate = selector.getMinSupportedRefreshRate(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); + + EXPECT_EQ(kMode60, minRate); + EXPECT_EQ(kMode90, performanceRate); + + const auto minRateByPolicy = selector.getMinRefreshRateByPolicy(); + const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(minRateByPolicy, minRate); + EXPECT_EQ(performanceRateByPolicy, performanceRate); +} + +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode60, minRate); + EXPECT_EQ(kMode60, minRate60); + EXPECT_EQ(kMode60, performanceRate60); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setActiveMode(kModeId90, 90_Hz); + + const auto minRate90 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode90_G1, performanceRate); + EXPECT_EQ(kMode90_G1, minRate90); + EXPECT_EQ(kMode90_G1, performanceRate90); +} + +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) { + auto selector = createSelector(kModes_60_90_4K, kModeId60); + + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode60, minRate); + EXPECT_EQ(kMode60, minRate60); + EXPECT_EQ(kMode60, performanceRate60); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setActiveMode(kModeId90, 90_Hz); + + const auto minRate90 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode90_4K, performanceRate); + EXPECT_EQ(kMode90_4K, minRate90); + EXPECT_EQ(kMode90_4K, performanceRate90); +} + +TEST_P(RefreshRateSelectorTest, twoModes_policyChange) { + auto selector = createSelector(kModes_60_90, kModeId60); + + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode60, minRate); + EXPECT_EQ(kMode90, performanceRate); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(kMode60, minRate60); + EXPECT_EQ(kMode60, performanceRate60); +} + +TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) { + auto selector = createSelector(kModes_60_90, kModeId60); + { + const auto& mode = selector.getActiveMode(); + EXPECT_EQ(mode.getId(), kModeId60); + } + + selector.setActiveMode(kModeId90, 90_Hz); + { + const auto& mode = selector.getActiveMode(); + EXPECT_EQ(mode.getId(), kModeId90); + } + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + { + const auto& mode = selector.getActiveMode(); + EXPECT_EQ(mode.getId(), kModeId90); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) { + { + auto selector = createSelector(kModes_60_72_90, kModeId72); + + // If there are no layers we select the default frame rate, which is the max of the primary + // range. + EXPECT_EQ(kMode90, selector.getBestFrameRateMode()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode()); + } + { + // We select max even when this will cause a non-seamless switch. + auto selector = createSelector(kModes_60_90_G1, kModeId60); + constexpr bool kAllowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching})); + EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode()); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId72); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].vote = LayerVoteType::ExplicitExact; + layers[0].desiredRefreshRate = 120_Hz; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}})); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::Min; + lr.name = "Min"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + lr.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + lr.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + lr.name = "45Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + lr.name = "30Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + lr.name = "24Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.name = ""; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + + lr.vote = LayerVoteType::Min; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + + lr.vote = LayerVoteType::Min; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}})); + lr.vote = LayerVoteType::Min; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::Min; + lr.name = "Min"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + lr.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + lr.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + lr.name = "45Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + lr.name = "30Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + lr.name = "24Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) { + auto selector = createSelector(kModes_60_72_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::Min; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 48_Hz; + lr2.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 48_Hz; + lr2.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "60Hz Heuristic"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "60Hz Heuristic"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "60Hz ExplicitDefault"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::Heuristic; + lr1.name = "24Hz Heuristic"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz ExplicitDefault"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz ExplicitDefault"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.name = "90Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) { + auto selector = + createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + auto& lr3 = layers[2]; + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "60Hz Heuristic"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "60Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "60Hz ExplicitDefault"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::Heuristic; + lr1.name = "24Hz Heuristic"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz ExplicitDefault"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "90Hz ExplicitDefault"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.name = "24Hz ExplicitDefault"; + lr2.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.name = "90Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "120Hz ExplicitDefault"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::ExplicitExact; + lr2.name = "120Hz ExplicitExact"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 10_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "120Hz ExplicitExact"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); + + lr1.desiredRefreshRate = 30_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 30_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.name = "30Hz ExplicitExactOrMultiple"; + lr3.vote = LayerVoteType::Heuristic; + lr3.desiredRefreshRate = 120_Hz; + lr3.name = "120Hz Heuristic"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) { + auto selector = createSelector(kModes_30_60, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::Min; + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 30_Hz; + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) { + auto selector = createSelector(kModes_30_60_72_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::Min; + lr.name = "Min"; + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + lr.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 90_Hz; + lr.vote = LayerVoteType::Heuristic; + lr.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Heuristic"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr.desiredRefreshRate = 45_Hz; + lr.name = "45Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr.desiredRefreshRate = 30_Hz; + lr.name = "30Hz Heuristic"; + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr.desiredRefreshRate = 24_Hz; + lr.name = "24Hz Heuristic"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr.desiredRefreshRate = 24_Hz; + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + lr.name = "24Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) { + auto selector = createSelector(kModes_30_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::Min; + lr2.vote = LayerVoteType::Max; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Min; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Min; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 24_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Max; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Max; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Heuristic; + lr1.desiredRefreshRate = 15_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Heuristic; + lr1.desiredRefreshRate = 30_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 45_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { + lr.desiredRefreshRate = Fps::fromValue(fps); + const auto mode = selector.getBestFrameRateMode(layers); + EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " + << to_string(mode->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) { + auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { + lr.desiredRefreshRate = Fps::fromValue(fps); + const auto mode = selector.getBestFrameRateMode(layers); + EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " + << to_string(mode->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::Heuristic; + lr1.desiredRefreshRate = 60_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 90_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::Heuristic; + lr1.desiredRefreshRate = 90_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 60_Hz; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) { + lr.desiredRefreshRate = Fps::fromValue(fps); + const auto mode = selector.getBestFrameRateMode(layers, {}); + EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses " + << to_string(mode->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 90_Hz; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 90_Hz; + lr2.name = "90Hz ExplicitDefault"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 30_Hz; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 90_Hz; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 30_Hz; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + // The other layer starts to provide buffers + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 90_Hz; + lr2.name = "90Hz Heuristic"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { + // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the + // different group. + auto selector = createSelector(kModes_30_60_90, kModeId60); + const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), + RefreshRateOrder::Descending); + + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) { + // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the + // different group. + auto selector = createSelector(kModes_30_60_90, kModeId60); + + const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), + RefreshRateOrder::Ascending); + + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) { + // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the + // different group. + auto selector = createSelector(kModes_30_60_90, kModeId72); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); + + const auto refreshRates = + selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending); + + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { + // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the + // different group. + auto selector = createSelector(kModes_30_60_90, kModeId72); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); + + const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, + RefreshRateOrder::Descending); + + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { + auto selector = createSelector(kModes_60_90, kModeId60); + + auto [refreshRates, signals] = selector.getRankedFrameRates({}, {}); + EXPECT_FALSE(signals.powerOnImminent); + + auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, + {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + std::tie(refreshRates, signals) = + selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true}); + EXPECT_TRUE(signals.powerOnImminent); + + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr1 = layers[0]; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + + std::tie(refreshRates, signals) = + selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true}); + EXPECT_TRUE(signals.powerOnImminent); + + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + std::tie(refreshRates, signals) = + selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false}); + EXPECT_FALSE(signals.powerOnImminent); + + expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90}, + {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; + } + }(); + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); + + for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, touchConsidered) { + auto selector = createSelector(kModes_60_90, kModeId60); + + auto [_, signals] = selector.getRankedFrameRates({}, {}); + EXPECT_FALSE(signals.touch); + + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true}); + EXPECT_TRUE(signals.touch); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz Heuristic"; + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); + EXPECT_TRUE(signals.touch); + + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitDefault"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz Heuristic"; + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); + EXPECT_FALSE(signals.touch); + + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz Heuristic"; + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); + EXPECT_TRUE(signals.touch); + + lr1.vote = LayerVoteType::ExplicitDefault; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitDefault"; + lr2.vote = LayerVoteType::Heuristic; + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz Heuristic"; + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); + EXPECT_FALSE(signals.touch); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) { + auto selector = createSelector(kModes_60_90_72_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + // Prepare a table with the vote and the expected refresh rate + const std::initializer_list<std::pair<Fps, Fps>> testCases = { + {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz}, + + {100_Hz, 90_Hz}, {90_Hz, 90_Hz}, {89_Hz, 90_Hz}, + + {80_Hz, 72_Hz}, {73_Hz, 72_Hz}, {72_Hz, 72_Hz}, {71_Hz, 72_Hz}, {70_Hz, 72_Hz}, + + {65_Hz, 60_Hz}, {60_Hz, 60_Hz}, {59_Hz, 60_Hz}, {58_Hz, 60_Hz}, + + {55_Hz, 90_Hz}, {50_Hz, 90_Hz}, {45_Hz, 90_Hz}, + + {42_Hz, 120_Hz}, {40_Hz, 120_Hz}, {39_Hz, 120_Hz}, + + {37_Hz, 72_Hz}, {36_Hz, 72_Hz}, {35_Hz, 72_Hz}, + + {30_Hz, 60_Hz}, + }; + + for (auto [desired, expected] : testCases) { + lr.vote = LayerVoteType::ExplicitDefault; + lr.desiredRefreshRate = desired; + + std::stringstream ss; + ss << "ExplicitDefault " << desired; + lr.name = ss.str(); + + EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) { + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + // Test that 23.976 will choose 24 if 23.976 is not supported + { + auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac), + kModeId60); + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + lr.desiredRefreshRate = 23.976_Hz; + lr.name = "ExplicitExactOrMultiple 23.976 Hz"; + EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId()); + } + + // Test that 24 will choose 23.976 if 24 is not supported + { + auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, + kMode60, kMode60Frac), + kModeId60); + + lr.desiredRefreshRate = 24_Hz; + lr.name = "ExplicitExactOrMultiple 24 Hz"; + EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId()); + } + + // Test that 29.97 will prefer 59.94 over 60 and 30 + { + auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60, + kMode60Frac), + kModeId60); + + lr.desiredRefreshRate = 29.97_Hz; + lr.name = "ExplicitExactOrMultiple 29.97 Hz"; + EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId()); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) { + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + // Test that voting for supported refresh rate will select this refresh rate + { + auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60); + + for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) { + lr.vote = LayerVoteType::ExplicitExact; + lr.desiredRefreshRate = desired; + std::stringstream ss; + ss << "ExplicitExact " << desired; + lr.name = ss.str(); + + EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps()); + } + } +} + +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { + auto selector = createSelector(kModes_60_90, kModeId90); + + constexpr FpsRange k90 = {90_Hz, 90_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitDefault; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz ExplicitDefault"; + lr.focused = true; + + const auto [rankedFrameRate, signals] = + selector.getRankedFrameRates(layers, {.touch = true, .idle = true}); + + EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60); + EXPECT_FALSE(signals.touch); +} + +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { + auto selector = createSelector(kModes_60_90, kModeId60); + + constexpr FpsRange k60 = {60_Hz, 60_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {k60, k60}, {k60_90, k60_90}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitDefault; + lr.desiredRefreshRate = 90_Hz; + lr.name = "90Hz ExplicitDefault"; + lr.focused = true; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true})); +} + +TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, + {.weight = 1.f}, + {.weight = 1.f}, + {.weight = 1.f}, + {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + auto& lr3 = layers[2]; + auto& lr4 = layers[3]; + auto& lr5 = layers[4]; + + lr1.desiredRefreshRate = 90_Hz; + lr1.name = "90Hz"; + lr1.focused = true; + + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz"; + lr2.focused = true; + + lr3.desiredRefreshRate = 72_Hz; + lr3.name = "72Hz"; + lr3.focused = true; + + lr4.desiredRefreshRate = 120_Hz; + lr4.name = "120Hz"; + lr4.focused = true; + + lr5.desiredRefreshRate = 30_Hz; + lr5.name = "30Hz"; + lr5.focused = true; + + auto expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{120_Hz, kMode120}, + {90_Hz, kMode90}, + {72_Hz, kMode72}, + {60_Hz, kMode60}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + + auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking; + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); + + for (size_t i = 0; i < expectedRanking.size(); ++i) { + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + lr1.vote = LayerVoteType::Max; + lr1.name = "Max"; + + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz"; + + lr3.desiredRefreshRate = 72_Hz; + lr3.name = "72Hz"; + + lr4.desiredRefreshRate = 90_Hz; + lr4.name = "90Hz"; + + lr5.desiredRefreshRate = 120_Hz; + lr5.name = "120Hz"; + + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{120_Hz, kMode120}, + {90_Hz, kMode90}, + {72_Hz, kMode72}, + {60_Hz, kMode60}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; + + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); + + for (size_t i = 0; i < expectedRanking.size(); ++i) { + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + lr1.vote = LayerVoteType::Heuristic; + lr1.desiredRefreshRate = 30_Hz; + lr1.name = "30Hz"; + + lr2.desiredRefreshRate = 120_Hz; + lr2.name = "120Hz"; + + lr3.desiredRefreshRate = 60_Hz; + lr3.name = "60Hz"; + + lr5.desiredRefreshRate = 72_Hz; + lr5.name = "72Hz"; + + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, + {60_Hz, kMode60}, + {90_Hz, kMode90}, + {120_Hz, kMode120}, + {72_Hz, kMode72}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; + + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); + + for (size_t i = 0; i < expectedRanking.size(); ++i) { + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } + + lr1.desiredRefreshRate = 120_Hz; + lr1.name = "120Hz"; + lr1.weight = 0.0f; + + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz"; + lr2.vote = LayerVoteType::NoVote; + + lr3.name = "60Hz-2"; + lr3.vote = LayerVoteType::Heuristic; + + lr4.vote = LayerVoteType::ExplicitExact; + + lr5.desiredRefreshRate = 120_Hz; + lr5.name = "120Hz-2"; + + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, + {60_Hz, kMode60}, + {120_Hz, kMode120}, + {72_Hz, kMode72}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; + + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); + + for (size_t i = 0; i < expectedRanking.size(); ++i) { + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; + } +} + +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { + auto selector = createSelector(kModes_60_90, kModeId90); + + constexpr FpsRange k90 = {90_Hz, 90_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); + + const auto [ranking, signals] = selector.getRankedFrameRates({}, {}); + EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90); + EXPECT_FALSE(signals.touch); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& lr = layers[0]; + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz ExplicitExactOrMultiple"; + lr.focused = false; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.focused = true; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::ExplicitDefault; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz ExplicitDefault"; + lr.focused = false; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.focused = true; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Heuristic; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Heuristic"; + lr.focused = false; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.focused = true; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Max; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Max"; + lr.focused = false; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.focused = true; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.vote = LayerVoteType::Min; + lr.desiredRefreshRate = 60_Hz; + lr.name = "60Hz Min"; + lr.focused = false; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + + lr.focused = true; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + // The default policy doesn't allow group switching. Verify that no + // group switches are performed. + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = 90_Hz; + layer.seamlessness = Seamlessness::SeamedAndSeamless; + layer.name = "90Hz ExplicitDefault"; + layer.focused = true; + + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = 90_Hz; + layer.seamlessness = Seamlessness::SeamedAndSeamless; + layer.name = "90Hz ExplicitDefault"; + layer.focused = true; + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + // Verify that we won't change the group if seamless switch is required. + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = 90_Hz; + layer.seamlessness = Seamlessness::OnlySeamless; + layer.name = "90Hz ExplicitDefault"; + layer.focused = true; + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + selector.setActiveMode(kModeId90, 90_Hz); + + // Verify that we won't do a seamless switch if we request the same mode as the default + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = 60_Hz; + layer.seamlessness = Seamlessness::OnlySeamless; + layer.name = "60Hz ExplicitDefault"; + layer.focused = true; + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + selector.setActiveMode(kModeId90, 90_Hz); + + // Verify that if the active mode is in another group and there are no layers with + // Seamlessness::SeamedAndSeamless, we should switch back to the default group. + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = 60_Hz; + layer.seamlessness = Seamlessness::Default; + layer.name = "60Hz ExplicitDefault"; + layer.focused = true; + + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + selector.setActiveMode(kModeId90, 90_Hz); + + // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with + // Seamlessness::OnlySeamless can't change the mode group. + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].seamlessness = Seamlessness::OnlySeamless; + layers[0].name = "60Hz ExplicitDefault"; + layers[0].focused = true; + + layers.push_back(LayerRequirement{.weight = 0.5f}); + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = 90_Hz; + layers[1].name = "90Hz ExplicitDefault"; + layers[1].focused = false; + + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + selector.setActiveMode(kModeId90, 90_Hz); + + // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with + // Seamlessness::Default can't change the mode group back to the group of the default + // mode. + // For example, this may happen when a video playback requests and gets a seamed switch, + // but another layer (with default seamlessness) starts animating. The animating layer + // should not cause a seamed switch. + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].seamlessness = Seamlessness::Default; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].focused = true; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].name = "60Hz ExplicitDefault"; + + layers.push_back(LayerRequirement{.weight = 0.1f}); + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = 90_Hz; + layers[1].focused = true; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].name = "90Hz ExplicitDefault"; + + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); + + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + selector.setActiveMode(kModeId90, 90_Hz); + + // Layer with Seamlessness::Default can change the mode group if there's an + // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens + // when in split screen mode the user switches between the two visible applications. + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].seamlessness = Seamlessness::Default; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].focused = true; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].name = "60Hz ExplicitDefault"; + + layers.push_back(LayerRequirement{.weight = 0.7f}); + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = 90_Hz; + layers[1].focused = false; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].name = "90Hz ExplicitDefault"; + + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) { + auto selector = createSelector(kModes_30_60, kModeId60); + + // Allow group switching. + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitExactOrMultiple; + layer.desiredRefreshRate = 60_Hz; + layer.seamlessness = Seamlessness::SeamedAndSeamless; + layer.name = "60Hz ExplicitExactOrMultiple"; + layer.focused = true; + + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); + + selector.setActiveMode(kModeId120, 120_Hz); + EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) { + auto selector = createSelector(kModes_25_30_50_60, kModeId60); + + // Allow group switching. + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault", + .vote = LayerVoteType::ExplicitDefault, + .desiredRefreshRate = 60_Hz, + .seamlessness = Seamlessness::SeamedAndSeamless, + .weight = 0.5f, + .focused = false}, + {.name = "25Hz ExplicitExactOrMultiple", + .vote = LayerVoteType::ExplicitExactOrMultiple, + .desiredRefreshRate = 25_Hz, + .seamlessness = Seamlessness::OnlySeamless, + .weight = 1.f, + .focused = true}}; + + EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId()); + + auto& seamedLayer = layers[0]; + seamedLayer.desiredRefreshRate = 30_Hz; + seamedLayer.name = "30Hz ExplicitDefault"; + selector.setActiveMode(kModeId30, 30_Hz); + + EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) { + auto selector = createSelector(kModes_60_90_G1, kModeId90); + + // Allow group switching. + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); + + std::vector<LayerRequirement> layers = { + {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}}; + + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); +} + +TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { + auto selector = createSelector(kModes_30_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + + struct Args { + bool touch = false; + bool focused = true; + }; + + // Returns the mode selected by getBestFrameRateMode for a single layer with the given + // arguments. + const auto getFrameRate = [&](LayerVoteType voteType, Fps fps, + Args args = {}) -> DisplayModeId { + layers[0].vote = voteType; + layers[0].desiredRefreshRate = fps; + layers[0].focused = args.focused; + return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId(); + }; + + constexpr FpsRange k30_60 = {30_Hz, 60_Hz}; + constexpr FpsRange k30_90 = {30_Hz, 90_Hz}; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}})); + + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId()); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); + EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); + EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); + + // Unfocused layers are not allowed to override primary range. + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false})); + EXPECT_EQ(kModeId60, + getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false})); + + // Touch boost should be restricted to the primary range. + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true})); + + // When we're higher than the primary range max due to a layer frame rate setting, touch boost + // shouldn't drag us back down to the primary range max. + EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true})); + EXPECT_EQ(kModeId60, + getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true})); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); +} + +TEST_P(RefreshRateSelectorTest, idle) { + auto selector = createSelector(kModes_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + + const auto getIdleDisplayModeId = [&](LayerVoteType voteType, + bool touchActive) -> DisplayModeId { + layers[0].vote = voteType; + layers[0].desiredRefreshRate = 90_Hz; + + const auto [ranking, signals] = + selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); + + // Refresh rate will be chosen by either touch state or idle state. + EXPECT_EQ(!touchActive, signals.idle); + return ranking.front().frameRateMode.modePtr->getId(); + }; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}})); + + // Idle should be lower priority than touch boost. + { + constexpr bool kTouchActive = true; + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId90, + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } + + // With no layers, idle should still be lower priority than touch boost. + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId()); + + // Idle should be higher precedence than other layer frame rate considerations. + selector.setActiveMode(kModeId90, 90_Hz); + + { + constexpr bool kTouchActive = false; + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId60, + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } + + // Idle should be applied rather than the active mode when there are no layers. + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId()); +} + +TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) { + auto selector = createSelector(kModes_60_90, kModeId60); + + for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { + const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps)); + const Fps expectedFrameRate = [fps] { + if (fps < 26.91f) return 24_Hz; + if (fps < 37.51f) return 30_Hz; + if (fps < 52.51f) return 45_Hz; + if (fps < 66.01f) return 60_Hz; + if (fps < 81.01f) return 72_Hz; + return 90_Hz; + }(); + + EXPECT_EQ(expectedFrameRate, knownFrameRate); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) { + auto selector = createSelector(kModes_60_90, kModeId60); + + struct Expectation { + Fps fps; + ftl::NonNull<DisplayModePtr> mode; + }; + + const std::initializer_list<Expectation> knownFrameRatesExpectations = { + {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90}, + {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90}, + }; + + // Make sure the test tests all the known frame rate + const auto& knownFrameRates = selector.knownFrameRates(); + const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(), + knownFrameRatesExpectations.begin(), + [](Fps fps, const Expectation& expected) { + return isApproxEqual(fps, expected.fps); + }); + EXPECT_TRUE(equal); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::Heuristic; + + for (const auto& [fps, mode] : knownFrameRatesExpectations) { + layer.desiredRefreshRate = fps; + EXPECT_EQ(mode, selector.getBestFrameRateMode(layers)); + } +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; + auto& explicitExactLayer = layers[0]; + auto& explicitExactOrMultipleLayer = layers[1]; + + explicitExactLayer.vote = LayerVoteType::ExplicitExact; + explicitExactLayer.name = "ExplicitExact"; + explicitExactLayer.desiredRefreshRate = 30_Hz; + + explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; + explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; + explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; + + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz, + selector.getBestScoredFrameRate(layers, {.touch = true}) + .frameRateMode); + + } else { + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, + selector.getBestScoredFrameRate(layers, {.touch = true}) + .frameRateMode); + } + + explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; + explicitExactLayer.desiredRefreshRate = 60_Hz; + + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } else { + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } + + explicitExactLayer.desiredRefreshRate = 72_Hz; + EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); + + explicitExactLayer.desiredRefreshRate = 90_Hz; + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); + + explicitExactLayer.desiredRefreshRate = 120_Hz; + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + using GlobalSignals = RefreshRateSelector::GlobalSignals; + const auto args = std::make_pair(std::vector<LayerRequirement>{}, + GlobalSignals{.touch = true, .idle = true}); + + const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{ + {90_Hz, kMode90}}}, + GlobalSignals{.touch = true}}; + + selector.mutableGetRankedRefreshRatesCache() = {args, result}; + + EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second)); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); + + EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache()); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; + RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true}; + + const auto result = selector.getRankedFrameRates(layers, globalSignals); + + const auto& cache = selector.mutableGetRankedRefreshRatesCache(); + ASSERT_TRUE(cache); + + EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals)); + EXPECT_EQ(cache->result, result); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) { + auto selector = createSelector(kModes_60_120, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; + auto& explicitExactLayer = layers[0]; + auto& explicitExactOrMultipleLayer = layers[1]; + + explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; + explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; + explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; + + explicitExactLayer.vote = LayerVoteType::ExplicitExact; + explicitExactLayer.name = "ExplicitExact"; + explicitExactLayer.desiredRefreshRate = 30_Hz; + + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true})); + } else { + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true})); + } + + explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote; + + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true})); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) { + auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}}; + auto& explicitDefaultLayer = layers[0]; + auto& explicitExactOrMultipleLayer = layers[1]; + + explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; + explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; + explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; + + explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault; + explicitDefaultLayer.name = "ExplicitDefault"; + explicitDefaultLayer.desiredRefreshRate = 59.94_Hz; + + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); +} + +// b/190578904 +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) { + if (g_noSlowTests) { + GTEST_SKIP(); + } + + const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue(); + constexpr int kMaxRefreshRate = 240; + + DisplayModes displayModes; + for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { + const DisplayModeId modeId(fps); + displayModes.try_emplace(modeId, + createDisplayMode(modeId, + Fps::fromValue(static_cast<float>(fps)))); + } + + const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate)); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { + layers[0].desiredRefreshRate = fps; + layers[0].vote = vote; + EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue()) + << "Failed for " << ftl::enum_string(vote); + }; + + for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { + const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); + testRefreshRate(refreshRate, LayerVoteType::Heuristic); + testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault); + testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple); + testRefreshRate(refreshRate, LayerVoteType::ExplicitExact); + } +} + +// b/190578904 +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) { + constexpr DisplayModeId kActiveModeId{0}; + DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz), + createDisplayMode(DisplayModeId(1), 53_Hz), + createDisplayMode(DisplayModeId(2), 55_Hz), + createDisplayMode(DisplayModeId(3), 60_Hz)); + + const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; + const auto selector = createSelector(std::move(displayModes), kActiveModeId); + + const std::vector<LayerRequirement> layers = { + { + .vote = LayerVoteType::ExplicitDefault, + .desiredRefreshRate = 43_Hz, + .seamlessness = Seamlessness::SeamedAndSeamless, + .weight = 0.41f, + }, + { + .vote = LayerVoteType::ExplicitExactOrMultiple, + .desiredRefreshRate = 53_Hz, + .seamlessness = Seamlessness::SeamedAndSeamless, + .weight = 0.41f, + }, + }; + + EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps()); +} + +TEST_P(RefreshRateSelectorTest, modeComparison) { + EXPECT_LT(kMode60->getFps(), kMode90->getFps()); + EXPECT_GE(kMode60->getFps(), kMode60->getFps()); + EXPECT_GE(kMode90->getFps(), kMode90->getFps()); +} + +TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) { + using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; + + auto selector = createSelector(kModes_60_90, kModeId90); + + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); +} + +TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) { + using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; + + auto selector = createSelector(kModes_60_120, kModeId120); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId30); + + const auto frameRate = 30_Hz; + Fps displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); + + selector.setActiveMode(kModeId60, 60_Hz); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); + + selector.setActiveMode(kModeId72, 72_Hz); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); + + selector.setActiveMode(kModeId90, 90_Hz); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); + + selector.setActiveMode(kModeId120, 120_Hz); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); + + selector.setActiveMode(kModeId90, 90_Hz); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz)); + + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 23.976_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(30_Hz, 29.97_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz)); +} + +TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) { + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz)); + + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 30_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 29.97_Hz)); + + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 60_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 59.94_Hz)); + + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 60_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 29.97_Hz)); + + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 30_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 59.94_Hz)); + + const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}; + for (auto refreshRate : refreshRates) { + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(refreshRate, refreshRate)); + } + + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 25_Hz)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(23.978_Hz, 25_Hz)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz)); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + + layers[0].vote = LayerVoteType::NoVote; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Min; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Max; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Heuristic; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) { + if (GetParam() != Config::FrameRateOverride::Disabled) { + return; + } + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + + layers[0].vote = LayerVoteType::ExplicitDefault; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::ExplicitExact; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + + layers[0].vote = LayerVoteType::ExplicitDefault; + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExact; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + + layers[0].name = "Test layer 1234"; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + layers[1].name = "Test layer 5678"; + layers[1].desiredRefreshRate = 30_Hz; + layers[1].vote = LayerVoteType::ExplicitDefault; + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + + EXPECT_EQ(2u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); + + layers[1].vote = LayerVoteType::Heuristic; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[1].ownerUid = 1234; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExact; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_60_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 30_Hz; + + const auto expetedFps = + GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExact; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); +} + +TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) { + auto selector = createSelector(kModes_60_120, kModeId120); + + // The render frame rate cannot be greater than the physical refresh rate + { + const FpsRange physical = {60_Hz, 60_Hz}; + const FpsRange render = {60_Hz, 120_Hz}; + EXPECT_EQ(SetPolicyResult::Invalid, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + } +} + +TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) { + auto selector = createSelector(kModes_60_120, kModeId120); + + { + const FpsRange physical = {0_Hz, 120_Hz}; + const FpsRange render = {0_Hz, 60_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + const auto expectedMaxMode = + GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60; + EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy()); + } + + { + const FpsRange physical = {0_Hz, 120_Hz}; + const FpsRange render = {120_Hz, 120_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy()); + } +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {60_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + layers[0].name = "30Hz"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {30_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {30_Hz, 30_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + layers[0].name = "60Hz"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); +} + +TEST_P(RefreshRateSelectorTest, renderFrameRates) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + // [renderRate, refreshRate] + const auto expected = []() -> std::vector<std::pair<Fps, Fps>> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, 30_Hz}, + {60_Hz, 60_Hz}, + {72_Hz, 72_Hz}, + {90_Hz, 90_Hz}, + {120_Hz, 120_Hz}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz}, + {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}}; + } + }(); + + const auto& primaryRefreshRates = selector.getPrimaryFrameRates(); + ASSERT_EQ(expected.size(), primaryRefreshRates.size()); + + for (size_t i = 0; i < expected.size(); i++) { + const auto [expectedRenderRate, expectedRefreshRate] = expected[i]; + EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps); + EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + auto selector = createSelector(kModes_60_120, kModeId60); + + constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz}; + constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz}; + + constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz, + /*render*/ k0_120Hz}; + + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode); + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz, + /*render*/ k0_120Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode); + + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz, + /*render*/ k0_60Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode); + + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz, + /*render*/ k0_60Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode); +} + +TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) { + auto selector = createSelector(kModes_60_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + + const auto expectedRenderRate = + GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz; + + layer.name = "30Hz ExplicitDefault"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::ExplicitDefault; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); + + layer.name = "30Hz Heuristic"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::Heuristic; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); + + layer.name = "30Hz ExplicitExactOrMultiple"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::ExplicitExactOrMultiple; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); +} + +TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) { + auto selector = createSelector(kModes_35_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + + const auto getIdleDisplayModeId = [&](LayerVoteType voteType, + bool touchActive) -> DisplayModeId { + layers[0].vote = voteType; + layers[0].desiredRefreshRate = 90_Hz; + + const auto [ranking, signals] = + selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); + + // Refresh rate will be chosen by either touch state or idle state. + EXPECT_EQ(!touchActive, signals.idle); + return ranking.front().frameRateMode.modePtr->getId(); + }; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}})); + + // With no layers, idle should still be lower priority than touch boost. + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId()); + + // Idle should be higher precedence than other layer frame rate considerations. + selector.setActiveMode(kModeId90, 90_Hz); + { + constexpr bool kTouchActive = false; + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId35, + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } + + // Idle should be applied rather than the active mode when there are no layers. + EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId()); +} + +TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) { + auto selector = createSelector(kModes_60_120, kModeId120); + + constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity()); + + using namespace fps_approx_ops; + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}}); + + // With no layers, idle should still be lower priority than touch boost. + EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy()); +} + +} // namespace +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp index f19e55409c..409e1ef5d7 100644 --- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp @@ -106,40 +106,6 @@ TEST_F(RegionSamplingTest, bounds_checking) { testing::Eq(0.0)); } -// workaround for b/133849373 -TEST_F(RegionSamplingTest, orientation_90) { - std::generate(buffer.begin(), buffer.end(), - [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); - - Rect tl_region{0, 0, 4, 4}; - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, - tl_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, - tl_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, - tl_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, - tl_region), - testing::Eq(0.0)); - - Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight}; - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, - br_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, - br_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, - br_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, - br_region), - testing::Eq(1.0)); -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 93c809e2fd..3ee53c94c2 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -21,7 +21,7 @@ #include <mutex> #include "Scheduler/EventThread.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -41,14 +41,13 @@ namespace { using MockEventThread = android::mock::EventThread; using MockLayer = android::mock::MockLayer; -constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); - class SchedulerTest : public testing::Test { protected: class MockEventThreadConnection : public android::EventThreadConnection { public: explicit MockEventThreadConnection(EventThread* eventThread) - : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {} + : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0), + ResyncCallback()) {} ~MockEventThreadConnection() = default; MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel)); @@ -58,14 +57,31 @@ protected: SchedulerTest(); - static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz); - static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz); - - std::shared_ptr<RefreshRateConfigs> mConfigs = - std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId()); + static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u); + static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 = + ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz)); + static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120); + + static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u); + static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 = + ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz)); + static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120); + + static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u); + static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz)); + static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60); + + std::shared_ptr<RefreshRateSelector> mSelector = + std::make_shared<RefreshRateSelector>(makeModes(kDisplay1Mode60), + kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback}; + TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback}; ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; @@ -79,7 +95,7 @@ SchedulerTest::SchedulerTest() { mEventThread = eventThread.get(); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); - mEventThreadConnection = new MockEventThreadConnection(mEventThread); + mEventThreadConnection = sp<MockEventThreadConnection>::make(mEventThread); // createConnection call to scheduler makes a createEventConnection call to EventThread. Make // sure that call gets executed and returns an EventThread::Connection object. @@ -104,7 +120,7 @@ TEST_F(SchedulerTest, invalidConnectionHandle) { // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); - mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false); + mScheduler->onHotplugReceived(handle, kDisplayId1, false); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); mScheduler->onScreenAcquired(handle); @@ -128,8 +144,8 @@ TEST_F(SchedulerTest, validConnectionHandle) { ASSERT_EQ(mEventThreadConnection, connection); EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle)); - EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1); - mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false); + EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1); + mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); mScheduler->onScreenAcquired(mConnectionHandle); @@ -165,7 +181,7 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); - EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0); + EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0); mScheduler->chooseRefreshRateForContent(); } @@ -174,8 +190,10 @@ TEST_F(SchedulerTest, updateDisplayModes) { sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); ASSERT_EQ(1u, mScheduler->layerHistorySize()); - mScheduler->setRefreshRateConfigs( - std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId())); + // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes. + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); @@ -192,14 +210,16 @@ TEST_F(SchedulerTest, dispatchCachedReportedMode) { TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) .setId(DisplayModeId(111)) - .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID) + .setPhysicalDisplayId(kDisplayId1) .setVsyncPeriod(111111) .build(); // If the handle is incorrect, the function should return before // onModeChange is called. ConnectionHandle invalidHandle = {.id = 123}; - EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode)); + EXPECT_NO_FATAL_FAILURE( + mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, + {90_Hz, ftl::as_non_null(mode)})); EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); } @@ -214,12 +234,13 @@ TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { } MATCHER(Is120Hz, "") { - return isApproxEqual(arg->getFps(), 120_Hz); + return isApproxEqual(arg.front().mode.fps, 120_Hz); } TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { - mScheduler->setRefreshRateConfigs( - std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId())); + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true)); @@ -232,12 +253,146 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); - EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1); + EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1); mScheduler->chooseRefreshRateForContent(); // No-op if layer requirements have not changed. - EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0); + EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0); mScheduler->chooseRefreshRateForContent(); } +TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); + + std::vector<RefreshRateSelector::LayerRequirement> layers = + std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); + mScheduler->setContentRequirements(layers); + GlobalSignals globalSignals = {.idle = true}; + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + using DisplayModeChoice = TestableScheduler::DisplayModeChoice; + + auto modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + + auto choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals)); + + globalSignals = {.idle = false}; + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); + + globalSignals = {.touch = true}; + mScheduler->replaceTouchTimer(10); + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); + + mScheduler->unregisterDisplay(kDisplayId1); + EXPECT_FALSE(mScheduler->hasRefreshRateSelectors()); +} + +TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); + mScheduler->registerDisplay(kDisplayId2, + std::make_shared<RefreshRateSelector>(kDisplay2Modes, + kDisplay2Mode60->getId())); + + using DisplayModeChoice = TestableScheduler::DisplayModeChoice; + TestableScheduler::DisplayModeChoiceMap expectedChoices; + + { + const GlobalSignals globalSignals = {.idle = true}; + expectedChoices = + ftl::init::map<const PhysicalDisplayId&, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{60_Hz, kDisplay1Mode60}, + globalSignals)(kDisplayId2, + FrameRateMode{60_Hz, + kDisplay2Mode60}, + globalSignals); + + std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f}, + {.weight = 1.f}}; + mScheduler->setContentRequirements(layers); + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + const auto actualChoices = mScheduler->chooseDisplayModes(); + EXPECT_EQ(expectedChoices, actualChoices); + } + { + const GlobalSignals globalSignals = {.idle = false}; + expectedChoices = + ftl::init::map<const PhysicalDisplayId&, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{120_Hz, kDisplay1Mode120}, + globalSignals)(kDisplayId2, + FrameRateMode{120_Hz, + kDisplay2Mode120}, + globalSignals); + + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + const auto actualChoices = mScheduler->chooseDisplayModes(); + EXPECT_EQ(expectedChoices, actualChoices); + } + { + const GlobalSignals globalSignals = {.touch = true}; + mScheduler->replaceTouchTimer(10); + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + expectedChoices = + ftl::init::map<const PhysicalDisplayId&, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{120_Hz, kDisplay1Mode120}, + globalSignals)(kDisplayId2, + FrameRateMode{120_Hz, + kDisplay2Mode120}, + globalSignals); + + const auto actualChoices = mScheduler->chooseDisplayModes(); + EXPECT_EQ(expectedChoices, actualChoices); + } + { + // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal. + mScheduler + ->registerDisplay(kDisplayId3, + std::make_shared<RefreshRateSelector>(kDisplay3Modes, + kDisplay3Mode60->getId())); + + const GlobalSignals globalSignals = {.touch = true}; + mScheduler->replaceTouchTimer(10); + mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); + + expectedChoices = ftl::init::map< + const PhysicalDisplayId&, + DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60}, + globalSignals)(kDisplayId2, + FrameRateMode{60_Hz, kDisplay2Mode60}, + globalSignals)(kDisplayId3, + FrameRateMode{60_Hz, + kDisplay3Mode60}, + globalSignals); + + const auto actualChoices = mScheduler->chooseDisplayModes(); + EXPECT_EQ(expectedChoices, actualChoices); + } +} + } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index b9a5f36794..6adcd5259d 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -24,8 +24,6 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#include "BufferStateLayer.h" -#include "EffectLayer.h" #include "Layer.h" // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" @@ -78,11 +76,11 @@ SetFrameRateTest::SetFrameRateTest() { } void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) { - layer.get()->addChild(child.get()); + layer->addChild(child); } void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) { - layer.get()->removeChild(child.get()); + layer->removeChild(child); } void SetFrameRateTest::commitTransaction() { @@ -374,11 +372,6 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { const auto& layerFactory = GetParam(); auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - if (!parent->isVisible()) { - // This is a hack as all the test layers except EffectLayer are not visible, - // but since the logic is unified in Layer, it should be fine. - return; - } auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); addChild(parent, child); @@ -390,8 +383,8 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); - const auto configs = mFlinger.mutableScheduler().refreshRateConfigs(); - const auto summary = history.summarize(*configs, 0); + const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector(); + const auto summary = history.summarize(*selectorPtr, 0); ASSERT_EQ(2u, summary.size()); EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index f7d34ac5da..6a9c970bc3 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -30,9 +30,6 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { // -------------------------------------------------------------------- // Call Expectations - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - // -------------------------------------------------------------------- // Invocation @@ -61,9 +58,6 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { // -------------------------------------------------------------------- // Call Expectations - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - // -------------------------------------------------------------------- // Invocation int64_t oldId = IPCThreadState::self()->clearCallingIdentity(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp index c7e61c96d6..93a3811172 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp @@ -37,9 +37,6 @@ TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) { // -------------------------------------------------------------------- // Call Expectations - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - // Destroying the display commits a display transaction. EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); @@ -65,7 +62,7 @@ TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) { // -------------------------------------------------------------------- // Preconditions - sp<BBinder> displayToken = new BBinder(); + sp<BBinder> displayToken = sp<BBinder>::make(); // -------------------------------------------------------------------- // Invocation diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 5872a472cd..074bf8cd1e 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ +#include "mock/MockDisplayModeSpecs.h" #include "mock/MockEventThread.h" #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" #include "DisplayTransactionTestHelpers.h" +#include <ftl/fake_guard.h> #include <scheduler/Fps.h> namespace android { @@ -40,18 +42,17 @@ public: PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this); PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); - mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K); + auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - { - DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K); - auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); + setupScheduler(selectorPtr); - mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) - .setDisplayModes(std::move(modes), kModeId60, std::move(configs)) - .inject(); - } + mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + mFlinger.configureAndCommit(); - setupScheduler(mDisplay->holdRefreshRateConfigs()); + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) + .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr)) + .inject(); // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy // will call setActiveConfig instead of setActiveConfigWithConstraints. @@ -60,7 +61,7 @@ public: } protected: - void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>); + void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>); sp<DisplayDevice> mDisplay; mock::EventThread* mAppEventThread; @@ -80,20 +81,22 @@ protected: }; void DisplayModeSwitchingTest::setupScheduler( - std::shared_ptr<scheduler::RefreshRateConfigs> configs) { + std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr) { auto eventThread = std::make_unique<mock::EventThread>(); mAppEventThread = eventThread.get(); auto sfEventThread = std::make_unique<mock::EventThread>(); EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -106,21 +109,24 @@ void DisplayModeSwitchingTest::setupScheduler( mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - std::move(configs)); + std::move(selectorPtr)); } TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) { + ftl::FakeGuard guard(kMainThreadContext); + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), false, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; @@ -133,30 +139,34 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe Mock::VerifyAndClearExpectations(mComposer); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that the next commit will complete the mode change and send // a onModeChanged event to the framework. - EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90)); + EXPECT_CALL(*mAppEventThread, + onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); mFlinger.commit(); Mock::VerifyAndClearExpectations(mAppEventThread); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90); } TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) { + ftl::FakeGuard guard(kMainThreadContext); + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - true, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), true, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. @@ -166,25 +176,29 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefres hal::HWConfigId(kModeId90.value()), _, _)) .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); - EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90)); + EXPECT_CALL(*mAppEventThread, + onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); mFlinger.commit(); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90); } TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { + ftl::FakeGuard guard(kMainThreadContext); + // Test that if we call setDesiredDisplayModeSpecs while a previous mode change // is still being processed the later call will be respected. ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), false, 0, + 120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_CALL(*mComposer, @@ -194,11 +208,12 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(), - false, 0.f, 180.f, 0.f, 180.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId120.value(), false, 0, + 180)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120); EXPECT_CALL(*mComposer, setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, @@ -208,26 +223,29 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120); mFlinger.commit(); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId120); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120); } TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) { + ftl::FakeGuard guard(kMainThreadContext); + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. @@ -262,7 +280,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh mDisplay = mFlinger.getDisplay(displayToken); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp index 9ac29071b2..94d517a3c3 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp @@ -90,15 +90,12 @@ void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessi Case::HdrSupport::setupComposerCallExpectations(this); Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); expectHotplugReceived<Case, true>(mEventThread); expectHotplugReceived<Case, true>(mSFEventThread); } template <typename Case> void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() { - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - expectHotplugReceived<Case, false>(mEventThread); expectHotplugReceived<Case, false>(mSFEventThread); } @@ -118,9 +115,7 @@ void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& d ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); - expectedPhysical = {.id = *displayId, - .type = *connectionType, - .hwcDisplayId = *hwcDisplayId}; + expectedPhysical = {.id = *displayId, .hwcDisplayId = *hwcDisplayId}; } // The display should have been set up in the current display state @@ -145,10 +140,13 @@ void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() { const auto displayId = Case::Display::DISPLAY_ID::get(); ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId); - ASSERT_TRUE(displayTokenOpt); + const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId); + ASSERT_TRUE(displayOpt); + + const auto& display = displayOpt->get(); + EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, display.snapshot().connectionType()); - verifyDisplayIsConnected<Case>(displayTokenOpt->get()); + verifyDisplayIsConnected<Case>(display.token()); } void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) { @@ -175,7 +173,7 @@ void DisplayTransactionCommitTest::processesHotplugConnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); + mFlinger.configureAndCommit(); // -------------------------------------------------------------------- // Postconditions @@ -204,7 +202,7 @@ void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); + mFlinger.configureAndCommit(); // -------------------------------------------------------------------- // Postconditions @@ -239,7 +237,7 @@ void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); + mFlinger.configureAndCommit(); // -------------------------------------------------------------------- // Postconditions @@ -247,10 +245,10 @@ void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() { // HWComposer should not have an entry for the display EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - // SF should not have a display token. + // SF should not have a PhysicalDisplay. const auto displayId = Case::Display::DISPLAY_ID::get(); ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId)); + ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId)); // The existing token should have been removed. verifyDisplayIsNotConnected(existing.token()); @@ -321,7 +319,7 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimar // -------------------------------------------------------------------- // Invocation - mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); + mFlinger.configureAndCommit(); // -------------------------------------------------------------------- // Postconditions @@ -329,10 +327,10 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimar // HWComposer should not have an entry for the display EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - // SF should not have a display token. + // SF should not have a PhysicalDisplay. const auto displayId = Case::Display::DISPLAY_ID::get(); ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId)); + ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId)); }(), testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); } @@ -366,7 +364,7 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimar // -------------------------------------------------------------------- // Invocation - mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); + mFlinger.configureAndCommit(); // -------------------------------------------------------------------- // Postconditions @@ -376,9 +374,9 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimar const auto displayId = Case::Display::DISPLAY_ID::get(); ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId); - ASSERT_TRUE(displayTokenOpt); - EXPECT_NE(existing.token(), displayTokenOpt->get()); + const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId); + ASSERT_TRUE(displayOpt); + EXPECT_NE(existing.token(), displayOpt->get().token()); // A new display should be connected in its place. verifyPhysicalDisplayIsConnected<Case>(); @@ -408,12 +406,12 @@ TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) { // A virtual display was added to the current state, and it has a // surface(producer) - sp<BBinder> displayToken = new BBinder(); + sp<BBinder> displayToken = sp<BBinder>::make(); DisplayDeviceState state; state.isSecure = static_cast<bool>(Case::Display::SECURE); - sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()}; + sp<mock::GraphicBufferProducer> surface{sp<mock::GraphicBufferProducer>::make()}; state.surface = surface; mFlinger.mutableCurrentState().displays.add(displayToken, state); @@ -479,7 +477,7 @@ TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) // A virtual display was added to the current state, but it does not have a // surface. - sp<BBinder> displayToken = new BBinder(); + sp<BBinder> displayToken = sp<BBinder>::make(); DisplayDeviceState state; state.isSecure = static_cast<bool>(Case::Display::SECURE); @@ -656,9 +654,11 @@ TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) { // Preconditions // A display is set up - auto nativeWindow = new mock::NativeWindow(); - auto displaySurface = new compositionengine::mock::DisplaySurface(); - sp<GraphicBuffer> buf = new GraphicBuffer(); + auto nativeWindow = sp<mock::NativeWindow>::make(); + auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make(); + sp<GraphicBuffer> buf = + + sp<GraphicBuffer>::make(); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.setNativeWindow(nativeWindow); display.setDisplaySurface(displaySurface); @@ -701,9 +701,9 @@ TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) { // Preconditions // A display is set up - auto nativeWindow = new mock::NativeWindow(); - auto displaySurface = new compositionengine::mock::DisplaySurface(); - sp<GraphicBuffer> buf = new GraphicBuffer(); + auto nativeWindow = sp<mock::NativeWindow>::make(); + auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make(); + sp<GraphicBuffer> buf = sp<GraphicBuffer>::make(); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.setNativeWindow(nativeWindow); display.setDisplaySurface(displaySurface); @@ -750,9 +750,9 @@ TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStac // Preconditions // A display is set up - auto nativeWindow = new mock::NativeWindow(); - auto displaySurface = new compositionengine::mock::DisplaySurface(); - sp<GraphicBuffer> buf = new GraphicBuffer(); + auto nativeWindow = sp<mock::NativeWindow>::make(); + auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make(); + sp<GraphicBuffer> buf = sp<GraphicBuffer>::make(); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.setNativeWindow(nativeWindow); display.setDisplaySurface(displaySurface); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp new file mode 100644 index 0000000000..0e149d2bfb --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2020 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" + +#include "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + +class ExcludeDolbyVisionTest : public DisplayTransactionTest { +public: + void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) { + DisplayModes modes; + for (DisplayModePtr displayMode : displayModePtrs) { + modes.try_emplace(displayMode->getId(), displayMode); + } + + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) + .setDisplayModes(std::move(modes), displayModePtrs[0]->getId()) + .inject(); + mDisplay->overrideHdrTypes(types); + } + +protected: + sp<DisplayDevice> mDisplay; + + static constexpr DisplayModeId modeId1080p60{0}; + static constexpr DisplayModeId modeId4k30{1}; + static constexpr DisplayModeId modeId4k60{2}; + + static inline const DisplayModePtr mode1080p60 = + createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080)); + static inline const DisplayModePtr mode4k30 = + createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160)); + static inline const DisplayModePtr mode4k30NonStandard = + createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160)); + static inline const DisplayModePtr mode4k60 = + createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160)); + + const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30, + ui::Hdr::HDR10_PLUS}; +}; + +TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) { + injectDisplayModes({mode4k60}); + ui::DynamicDisplayInfo info; + mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info); + + std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes; + + ASSERT_EQ(1, displayModes.size()); + ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(), + displayModes[0].supportedHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; })); + ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1); +} + +TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) { + injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard}); + ui::DynamicDisplayInfo info; + mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info); + + std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes; + + ASSERT_EQ(2, displayModes.size()); + for (size_t i = 0; i < displayModes.size(); i++) { + ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(), + displayModes[i].supportedHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; })); + ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(), + displayModes[i].supportedHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; })); + ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2); + } +} + +TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) { + injectDisplayModes({mode4k60}); + ui::DynamicDisplayInfo info; + mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info); + + std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes(); + + ASSERT_EQ(2, displayHdrTypes.size()); + ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; })); + ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(), + [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; })); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp index 0171f1b41d..5951c9893f 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp @@ -95,7 +95,7 @@ TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) { } TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) { - sp<BBinder> notInternalDisplayToken = new BBinder(); + sp<BBinder> notInternalDisplayToken = sp<BBinder>::make(); ui::DisplayPrimaries primaries; populateDummyDisplayNativePrimaries(primaries); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index c9a2b00fa3..1210d0b472 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -20,41 +20,18 @@ #include "DisplayTransactionTestHelpers.h" namespace android { -namespace { class HotplugTest : public DisplayTransactionTest {}; -TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) { - constexpr HWDisplayId hwcDisplayId1 = 456; - constexpr HWDisplayId hwcDisplayId2 = 654; - - // -------------------------------------------------------------------- - // Preconditions - - // Set the main thread id so that the current thread does not appear to be - // the main thread. - mFlinger.mutableMainThreadId() = std::thread::id(); - - // -------------------------------------------------------------------- - // Call Expectations +TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) { + EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2); - // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - // Simulate two hotplug events (a connect and a disconnect) + constexpr HWDisplayId hwcDisplayId1 = 456; mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED); - mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED); - - // -------------------------------------------------------------------- - // Postconditions - // The display transaction needed flag should be set. - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + constexpr HWDisplayId hwcDisplayId2 = 654; + mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED); - // All events should be in the pending event queue. const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); ASSERT_EQ(2u, pendingEvents.size()); EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId); @@ -63,49 +40,83 @@ TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) { EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection); } -TEST_F(HotplugTest, processesEnqueuedEventsIfCalledOnMainThread) { +TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) { + EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + constexpr HWDisplayId displayId1 = 456; + mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED); + mFlinger.configure(); - // -------------------------------------------------------------------- - // Note: - // -------------------------------------------------------------------- - // This test case is a bit tricky. We want to verify that - // onComposerHalHotplug() calls processDisplayHotplugEventsLocked(), but we - // don't really want to provide coverage for everything the later function - // does as there are specific tests for it. - // -------------------------------------------------------------------- + // The configure stage should consume the hotplug queue and produce a display transaction. + EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); + EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); +} - // -------------------------------------------------------------------- - // Preconditions +TEST_F(HotplugTest, ignoresDuplicateDisconnection) { + // Inject a primary display. + PrimaryDisplayVariant::injectHwcDisplay(this); - // Set the main thread id so that the current thread does appear to be the - // main thread. - mFlinger.mutableMainThreadId() = std::this_thread::get_id(); + using ExternalDisplay = ExternalDisplayVariant; + ExternalDisplay::setupHwcHotplugCallExpectations(this); + ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this); - // -------------------------------------------------------------------- - // Call Expectations + // TODO(b/241286146): Remove this unnecessary call. + EXPECT_CALL(*mComposer, + setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); - // We expect a scheduled commit for the display transaction. + // A single commit should be scheduled for both configure calls. EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - // -------------------------------------------------------------------- - // Invocation + ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + mFlinger.configure(); - // Simulate a disconnect on a display id that is not connected. This should - // be enqueued by onComposerHalHotplug(), and dequeued by - // processDisplayHotplugEventsLocked(), but then ignored as invalid. - mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED); + EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); - // -------------------------------------------------------------------- - // Postconditions + // Disconnecting a display that was already disconnected should be a no-op. + ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + mFlinger.configure(); - // The display transaction needed flag should be set. - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + // The display should be scheduled for removal during the next commit. At this point, it should + // still exist but be marked as disconnected. + EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); + EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get())); +} - // There should be no event queued on return, as it should have been - // processed. - EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); +TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { + // Inject a primary display. + PrimaryDisplayVariant::injectHwcDisplay(this); + + using ExternalDisplay = ExternalDisplayVariant; + constexpr bool kFailedHotplug = true; + ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this); + + // Simulate a connect event that fails to load display modes due to HWC already having + // disconnected the display but SF yet having to process the queued disconnect event. + EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _)) + .WillRepeatedly(Return(Error::BAD_DISPLAY)); + + // TODO(b/241286146): Remove this unnecessary call. + EXPECT_CALL(*mComposer, + setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + + ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + mFlinger.configure(); + + // The hotplug should be rejected, so no HWComposer::DisplayData should be created. + EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); + + // Disconnecting a display that does not exist should be a no-op. + ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + mFlinger.configure(); + + EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); } -} // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp index ec7e8a7f82..4e9f293dc3 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp @@ -21,6 +21,7 @@ #include <thread> #include "DisplayTransactionTestHelpers.h" +#include "FakeDisplayInjector.h" #include <android/hardware/power/Boost.h> @@ -32,6 +33,8 @@ using android::hardware::power::Boost; TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { using namespace std::chrono_literals; + injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {}); + mFlinger.scheduler()->replaceTouchTimer(100); std::this_thread::sleep_for(10ms); // wait for callback to be triggered EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp index 37cf05ef64..f553a23f3c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp @@ -38,11 +38,6 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { // -------------------------------------------------------------------- // Call Expectations - // We expect the surface interceptor to possibly be used, but we treat it as - // disabled since it is called as a side effect rather than directly by this - // function. - EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false)); - // We expect a call to get the active display config. Case::Display::setupHwcGetActiveConfigCallExpectations(this); @@ -78,19 +73,8 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { auto displayDevice = primaryDisplay.mutableDisplayDevice(); EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode()); - // The display refresh period should be set in the orientedDisplaySpaceRect tracker. - FrameStats stats; - mFlinger.getAnimFrameTracker().getStats(&stats); - EXPECT_EQ(DEFAULT_VSYNC_PERIOD, stats.refreshPeriodNano); - // The display transaction needed flag should be set. EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); - - // The compositor timing should be set to default values - const auto& compositorTiming = mFlinger.getCompositorTiming(); - EXPECT_EQ(-DEFAULT_VSYNC_PERIOD, compositorTiming.deadline); - EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.interval); - EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.presentLatency); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index 2c9888dd8e..622717f290 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -60,8 +60,8 @@ protected: renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); sp<DisplayDevice> mDisplay; sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - new compositionengine::mock::DisplaySurface(); - mock::NativeWindow* mNativeWindow = new mock::NativeWindow(); + sp<compositionengine::mock::DisplaySurface>::make(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); mock::TimeStats* mTimeStats = new mock::TimeStats(); Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; Hwc2::mock::Composer* mComposer = nullptr; @@ -97,7 +97,6 @@ void SurfaceFlingerPowerHintTest::SetUp() { .setNativeWindow(mNativeWindow) .setPowerMode(hal::PowerMode::ON) .inject(); - mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken(); } void SurfaceFlingerPowerHintTest::setupScheduler() { @@ -106,13 +105,15 @@ void SurfaceFlingerPowerHintTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -131,22 +132,20 @@ void SurfaceFlingerPowerHintTest::setupScheduler() { TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); - const std::chrono::nanoseconds mockVsyncPeriod = 15ms; EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(1); - - const nsecs_t now = systemTime(); - const std::chrono::nanoseconds mockHwcRunTime = 20ms; EXPECT_CALL(*mDisplaySurface, prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc)) .Times(1); - EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)) - .WillOnce([mockHwcRunTime] { - std::this_thread::sleep_for(mockHwcRunTime); - return hardware::graphics::composer::V2_1::Error::NONE; - }); + EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] { + constexpr Duration kMockHwcRunTime = 20ms; + std::this_thread::sleep_for(kMockHwcRunTime); + return hardware::graphics::composer::V2_1::Error::NONE; + }); EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1); - static constexpr bool kVsyncId = 123; // arbitrary - mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); + + const TimePoint frameTime = scheduler::SchedulerClock::now(); + constexpr Period kMockVsyncPeriod = 15ms; + mFlinger.commitAndComposite(frameTime, VsyncId{123}, frameTime + kMockVsyncPeriod); } TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) { @@ -154,22 +153,20 @@ TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) { mDisplay->setPowerMode(hal::PowerMode::DOZE); - const std::chrono::nanoseconds mockVsyncPeriod = 15ms; EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0); - - const nsecs_t now = systemTime(); - const std::chrono::nanoseconds mockHwcRunTime = 20ms; EXPECT_CALL(*mDisplaySurface, prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc)) .Times(1); - EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)) - .WillOnce([mockHwcRunTime] { - std::this_thread::sleep_for(mockHwcRunTime); - return hardware::graphics::composer::V2_1::Error::NONE; - }); + EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] { + constexpr Duration kMockHwcRunTime = 20ms; + std::this_thread::sleep_for(kMockHwcRunTime); + return hardware::graphics::composer::V2_1::Error::NONE; + }); EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0); - static constexpr bool kVsyncId = 123; // arbitrary - mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); + + const TimePoint frameTime = scheduler::SchedulerClock::now(); + constexpr Period kMockVsyncPeriod = 15ms; + mFlinger.commitAndComposite(frameTime, VsyncId{123}, frameTime + kMockVsyncPeriod); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp index 7d9e22b23e..9c7f55b733 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp @@ -34,7 +34,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDis // Preconditions // We have an unknown display token not associated with a known display - sp<BBinder> displayToken = new BBinder(); + sp<BBinder> displayToken = sp<BBinder>::make(); // The requested display state references the unknown display. DisplayState state; @@ -95,7 +95,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNo display.inject(); // There is a surface that can be set. - sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); + sp<mock::GraphicBufferProducer> surface = sp<mock::GraphicBufferProducer>::make(); // The current display state has the surface set display.mutableCurrentDisplayState().surface = surface; @@ -132,7 +132,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceCh display.inject(); // There is a surface that can be set. - sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); + sp<mock::GraphicBufferProducer> surface = sp<mock::GraphicBufferProducer>::make(); // The current display state does not have a surface display.mutableCurrentDisplayState().surface = nullptr; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 583cf5f836..0fb8e2bbaa 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -140,7 +140,6 @@ struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); } }; @@ -155,7 +154,6 @@ struct TransitionOffToDozeSuspendVariant static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); } }; @@ -255,16 +253,12 @@ struct DisplayPowerCase { using DispSync = DispSyncVariant; using Transition = TransitionVariant; - static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) { + static sp<DisplayDevice> injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, + PowerMode mode) { Display::injectHwcDisplayWithNoDefaultCapabilities(test); - auto display = Display::makeFakeExistingDisplayInjector(test); - display.inject(); - display.mutableDisplayDevice()->setPowerMode(mode); - if (display.mutableDisplayDevice()->isInternal()) { - test->mFlinger.mutableActiveDisplayToken() = - display.mutableDisplayDevice()->getDisplayToken(); - } - + auto injector = Display::makeFakeExistingDisplayInjector(test); + const auto display = injector.inject(); + display->setPowerMode(mode); return display; } @@ -276,13 +270,6 @@ struct DisplayPowerCase { EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1); } - static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, - PowerMode mode) { - EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true)); - EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode))) - .Times(1); - } - static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) { // Any calls to get the active config will return a default value. EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _)) @@ -349,14 +336,12 @@ void SetPowerModeInternalTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Call Expectations - Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE); Case::Transition::template setupCallExpectations<Case>(this); // -------------------------------------------------------------------- // Invocation - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), - Case::Transition::TARGET_POWER_MODE); + mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE); // -------------------------------------------------------------------- // Postconditions @@ -499,5 +484,37 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDispla transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); } +TEST_F(SetPowerModeInternalTest, designatesLeaderDisplay) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // Inject a primary display with uninitialized power mode. + constexpr bool kInitPowerMode = false; + Case::Display::injectHwcDisplay<kInitPowerMode>(this); + auto injector = Case::Display::makeFakeExistingDisplayInjector(this); + injector.setPowerMode(std::nullopt); + const auto display = injector.inject(); + + // -------------------------------------------------------------------- + // Invocation + + // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been + // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal` + // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s). + constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42); + ASSERT_NE(display->getPhysicalId(), kPlaceholderId); + mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId); + + mFlinger.setPowerModeInternal(display, PowerMode::ON); + + // -------------------------------------------------------------------- + // Postconditions + + // The primary display should be designated as the leader. + EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId()); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index a0e078bf5e..c0796df6cb 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -17,6 +17,8 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <ftl/fake_guard.h> + #include "DisplayHardware/DisplayMode.h" #include "DisplayTransactionTestHelpers.h" @@ -34,16 +36,13 @@ struct WideColorP3ColorimetricSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = true; static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableHasWideColorDisplay() = true; + test->mFlinger.mutableSupportsWideColor() = true; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } static void setupComposerCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1); - EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})), - Return(Error::NONE))); EXPECT_CALL(*test->mComposer, getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _)) .WillOnce(DoAll(SetArgPointee<2>( @@ -197,10 +196,10 @@ public: template <typename Case> void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { - const sp<BBinder> displayToken = new BBinder(); + const sp<BBinder> displayToken = sp<BBinder>::make(); const sp<compositionengine::mock::DisplaySurface> displaySurface = - new compositionengine::mock::DisplaySurface(); - const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer(); + sp<compositionengine::mock::DisplaySurface>::make(); + const auto producer = sp<mock::GraphicBufferProducer>::make(); // -------------------------------------------------------------------- // Preconditions @@ -246,11 +245,20 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { .setDpiY(DEFAULT_DPI) .setGroup(0) .build(); + state.physical = {.id = *displayId, - .type = *connectionType, .hwcDisplayId = *hwcDisplayId, - .supportedModes = makeModes(activeMode), - .activeMode = std::move(activeMode)}; + .activeMode = activeMode}; + + ui::ColorModes colorModes; + if constexpr (Case::WideColorSupport::WIDE_COLOR_SUPPORTED) { + colorModes.push_back(ColorMode::DISPLAY_P3); + } + + mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId, + *connectionType, + makeModes(activeMode), + std::move(colorModes), std::nullopt); } state.isSecure = static_cast<bool>(Case::Display::SECURE); @@ -262,9 +270,8 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // -------------------------------------------------------------------- // Postconditions - ASSERT_TRUE(device != nullptr); + ASSERT_NE(nullptr, device); EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId()); - EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType()); EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual()); EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); @@ -280,9 +287,8 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { device->receivesInput()); if constexpr (Case::Display::CONNECTION_TYPE::value) { - EXPECT_EQ(1, device->getSupportedModes().size()); - EXPECT_NE(nullptr, device->getActiveMode()); - EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId()); + ftl::FakeGuard guard(kMainThreadContext); + EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId()); } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp new file mode 100644 index 0000000000..fed6a1ae56 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp @@ -0,0 +1,224 @@ +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/LayerMetadata.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/MockEventThread.h" +#include "mock/MockVsyncController.h" + +namespace android { + +using testing::_; +using testing::Return; +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test { +public: + SurfaceFlingerUpdateLayerMetadataSnapshotTest() { setupScheduler(); } + +protected: + void setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); + + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); + } + + sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) { + LayerCreationArgs args = + LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata}; + inOutlayerMetadata = args.metadata; + return sp<Layer>::make(args); + } + + TestableSurfaceFlinger mFlinger; +}; + +class LayerMetadataBuilder { +public: + LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {} + + LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) { + mLayerMetadata.setInt32(key, value); + return *this; + } + + LayerMetadata build() { return mLayerMetadata; } + +private: + LayerMetadata mLayerMetadata; +}; + +bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) { + return lhs.mMap == rhs.mMap; +} + +std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) { + stream << "LayerMetadata{"; + for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) { + if (it != layerMetadata.mMap.cbegin()) { + stream << ", "; + } + stream << layerMetadata.itemToString(it->first, ":"); + } + return stream << "}"; +} + +// Test that the snapshot's layer metadata is set. +TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) { + auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build(); + auto layer = createLayer("layer", layerMetadata); + mFlinger.mutableDrawingState().layersSortedByZ.add(layer); + + mFlinger.updateLayerMetadataSnapshot(); + + EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata); +} + +// Test that snapshot layer metadata is set by merging the child's metadata on top of its +// parent's metadata. +TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) { + auto layerAMetadata = LayerMetadataBuilder() + .setInt32(METADATA_OWNER_UID, 1) + .setInt32(METADATA_TASK_ID, 2) + .build(); + auto layerA = createLayer("parent", layerAMetadata); + auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build(); + auto layerB = createLayer("child", layerBMetadata); + layerA->addChild(layerB); + layerA->commitChildList(); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); + + mFlinger.updateLayerMetadataSnapshot(); + + EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata); + auto expectedChildMetadata = + LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build(); + EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata); +} + +// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of +// that parent's relative layer metadata. +TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) { + auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build(); + auto layerA = createLayer("relative-parent", layerAMetadata); + auto layerAHandle = layerA->getHandle(); + auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build(); + auto layerB = createLayer("relative-child", layerBMetadata); + layerB->setRelativeLayer(layerAHandle, 1); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerB); + + mFlinger.updateLayerMetadataSnapshot(); + + EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{}); + EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); +} + +// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within +// two other layers. +// +// Layer +// A +// / \ +// B D +// / +// C +// +// Z-order Relatives +// B <- D <- C +TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) { + auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build(); + auto layerA = createLayer("layer-a", layerAMetadata); + auto layerBMetadata = LayerMetadataBuilder() + .setInt32(METADATA_TASK_ID, 2) + .setInt32(METADATA_OWNER_PID, 3) + .build(); + auto layerB = createLayer("layer-b", layerBMetadata); + auto layerBHandle = layerB->getHandle(); + LayerMetadata layerCMetadata; + auto layerC = createLayer("layer-c", layerCMetadata); + auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build(); + auto layerD = createLayer("layer-d", layerDMetadata); + auto layerDHandle = layerD->getHandle(); + layerB->addChild(layerC); + layerA->addChild(layerB); + layerA->addChild(layerD); + layerC->setRelativeLayer(layerDHandle, 1); + layerD->setRelativeLayer(layerBHandle, 1); + layerA->commitChildList(); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); + + mFlinger.updateLayerMetadataSnapshot(); + + auto expectedLayerDRelativeMetadata = + LayerMetadataBuilder() + // From layer A, parent of relative parent + .setInt32(METADATA_OWNER_UID, 1) + // From layer B, relative parent + .setInt32(METADATA_TASK_ID, 2) + .setInt32(METADATA_OWNER_PID, 3) + // added by layer creation args + .setInt32(gui::METADATA_CALLING_UID, + layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0)) + .build(); + EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata); + auto expectedLayerCRelativeMetadata = + LayerMetadataBuilder() + // From layer A, parent of relative parent + .setInt32(METADATA_OWNER_UID, 1) + // From layer B, relative parent of relative parent + .setInt32(METADATA_OWNER_PID, 3) + // From layer D, relative parent + .setInt32(METADATA_TASK_ID, 4) + // added by layer creation args + .setInt32(gui::METADATA_CALLING_UID, + layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0)) + .build(); + EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata); +} + +TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, + updatesRelativeMetadataMultipleRelativeChildren) { + auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build(); + auto layerA = createLayer("layer-a", layerAMetadata); + auto layerAHandle = layerA->getHandle(); + LayerMetadata layerBMetadata; + auto layerB = createLayer("layer-b", layerBMetadata); + LayerMetadata layerCMetadata; + auto layerC = createLayer("layer-c", layerCMetadata); + layerB->setRelativeLayer(layerAHandle, 1); + layerC->setRelativeLayer(layerAHandle, 2); + layerA->commitChildList(); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerB); + mFlinger.mutableDrawingState().layersSortedByZ.add(layerC); + + mFlinger.updateLayerMetadataSnapshot(); + + EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{}); + EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); + EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 4708572dc4..b8a60638ae 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -17,6 +17,7 @@ #pragma once #include <Scheduler/Scheduler.h> +#include <ftl/fake_guard.h> #include <gmock/gmock.h> #include <gui/ISurfaceComposer.h> @@ -32,17 +33,19 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) + TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_unique<mock::VSyncTracker>(), std::move(configs), + std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr), callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, - std::unique_ptr<VSyncTracker> tracker, - std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) + std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateConfigs(std::move(configs)); + + const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); + registerDisplay(displayId, std::move(selectorPtr)); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { // Execute task to prevent broken promise exception on destruction. @@ -50,6 +53,7 @@ public: }); } + MOCK_METHOD(void, scheduleConfigure, (), (override)); MOCK_METHOD(void, scheduleFrame, (), (override)); MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override)); @@ -65,14 +69,39 @@ public: auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } + auto refreshRateSelector() { return leaderSelectorPtr(); } + + const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS { + return mRefreshRateSelectors; + } + + bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); } + + void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { + ftl::FakeGuard guard(kMainThreadContext); + Scheduler::registerDisplay(displayId, std::move(selectorPtr)); + } + + void unregisterDisplay(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + Scheduler::unregisterDisplay(displayId); + } + + std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS { + return mLeaderDisplayId; + } + + void setLeaderDisplay(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + Scheduler::setLeaderDisplay(displayId); + } + auto& mutableLayerHistory() { return mLayerHistory; } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size(); } - auto refreshRateConfigs() { return holdRefreshRateConfigs(); } - size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size(); } @@ -93,9 +122,27 @@ public: return mPolicy.touch == Scheduler::TouchState::Active; } + void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) { + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive; + mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset; + } + + void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) { + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.contentRequirements = std::move(layers); + } + + using Scheduler::DisplayModeChoice; + using Scheduler::DisplayModeChoiceMap; + + DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS { + return Scheduler::chooseDisplayModes(); + } + void dispatchCachedReportedMode() { std::lock_guard<std::mutex> lock(mPolicyLock); - return Scheduler::dispatchCachedReportedMode(); + Scheduler::dispatchCachedReportedMode(); } void clearCachedReportedMode() { @@ -103,14 +150,15 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { - return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { + Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } private: // ICompositor overrides: - bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } - void composite(nsecs_t, int64_t) override {} + void configure() override {} + bool commit(TimePoint, VsyncId, TimePoint) override { return false; } + void composite(TimePoint, VsyncId) override {} void sample() override {} }; diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 283f9ca77b..2117084bbf 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -27,23 +27,23 @@ #include <compositionengine/impl/Display.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/DisplaySurface.h> +#include <ftl/fake_guard.h> #include <gui/ScreenCaptureResults.h> -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" -#include "ContainerLayer.h" +#include <ui/DynamicDisplayInfo.h> #include "DisplayDevice.h" -#include "EffectLayer.h" #include "FakeVsyncConfiguration.h" #include "FrameTracer/FrameTracer.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "NativeWindowSurface.h" +#include "RenderArea.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerDefaultFactory.h" -#include "SurfaceInterceptor.h" #include "TestableScheduler.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -84,22 +84,18 @@ public: return std::make_unique<scheduler::FakePhaseOffsets>(); } - sp<SurfaceInterceptor> createSurfaceInterceptor() override { - return new android::impl::SurfaceInterceptor(); - } - sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { - return new StartPropertySetThread(timestampPropertyValue); + return sp<StartPropertySetThread>::make(timestampPropertyValue); } sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override { - return new DisplayDevice(creationArgs); + return sp<DisplayDevice>::make(creationArgs); } sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, std::string requestorName) override { - return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName); } void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, @@ -112,18 +108,6 @@ public: mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); } - sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer, - const sp<SurfaceFlinger>& flinger, - const wp<Layer>& layer) override { - return new MonitoredProducer(producer, flinger, layer); - } - - sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer, - renderengine::RenderEngine& renderEngine, - uint32_t textureName, Layer* layer) override { - return new BufferLayerConsumer(consumer, renderEngine, textureName, layer); - } - std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( const sp<IGraphicBufferProducer>& producer) override { if (!mCreateNativeWindowSurface) return nullptr; @@ -134,18 +118,12 @@ public: return compositionengine::impl::createCompositionEngine(); } - sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override { - return nullptr; - } - - sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override { - return nullptr; - } + sp<Layer> createBufferStateLayer(const LayerCreationArgs&) override { return nullptr; } - sp<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; } + sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; } - sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override { - return nullptr; + sp<LayerFE> createLayerFE(const std::string& layerName) override { + return sp<LayerFE>::make(layerName); } std::unique_ptr<FrameTracer> createFrameTracer() override { @@ -183,7 +161,6 @@ public: if (!mFlinger) { mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); } - mFlinger->mAnimationTransactionTimeout = ms2ns(10); } SurfaceFlinger* flinger() { return mFlinger.get(); } @@ -193,7 +170,8 @@ public: // functions. void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine)); + mFlinger->mRenderEngine = std::move(renderEngine); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { @@ -217,10 +195,10 @@ public: static constexpr struct TwoDisplayModes { } kTwoDisplayModes; - using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>; + using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>; using DisplayModesVariant = - std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>; + std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>; void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, @@ -229,9 +207,9 @@ public: SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, DisplayModesVariant modesVariant = kOneDisplayMode, bool useNiceMock = false) { - RefreshRateConfigsPtr configs; - if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) { - configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant)); + RefreshRateSelectorPtr selectorPtr; + if (std::holds_alternative<RefreshRateSelectorPtr>(modesVariant)) { + selectorPtr = std::move(std::get<RefreshRateSelectorPtr>(modesVariant)); } else { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -241,10 +219,10 @@ public: modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); } - configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); + selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); } - const auto fps = configs->getActiveMode()->getFps(); + const auto fps = selectorPtr->getActiveMode().fps; mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -262,12 +240,12 @@ public: mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), std::move(vsyncTracker), - std::move(configs), + std::move(selectorPtr), callback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(configs), callback); + std::move(selectorPtr), callback); } mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); @@ -310,7 +288,7 @@ public: const sp<NativeHandle>& sidebandStream) { layer->mDrawingState.sidebandStream = sidebandStream; layer->mSidebandStream = sidebandStream; - layer->editCompositionState()->sidebandStream = sidebandStream; + layer->editLayerSnapshot()->sidebandStream = sidebandStream; } void setLayerCompositionType(const sp<Layer>& layer, @@ -338,25 +316,29 @@ public: * Forwarding for functions being tested */ - nsecs_t commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVSyncTime) { - mFlinger->commit(frameTime, vsyncId, expectedVSyncTime); + void configure() { mFlinger->configure(); } + + void configureAndCommit() { + configure(); + commitTransactionsLocked(eDisplayTransactionNeeded); + } + + TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) { + mFlinger->commit(frameTime, vsyncId, expectedVsyncTime); return frameTime; } - nsecs_t commit(nsecs_t frameTime, int64_t vsyncId) { - std::chrono::nanoseconds period = 10ms; - return commit(frameTime, vsyncId, frameTime + period.count()); + TimePoint commit(TimePoint frameTime, VsyncId vsyncId) { + return commit(frameTime, vsyncId, frameTime + Period(10ms)); } - nsecs_t commit() { - const nsecs_t now = systemTime(); - const nsecs_t expectedVsyncTime = now + 10'000'000; - return commit(now, kVsyncId, expectedVsyncTime); + TimePoint commit() { + const TimePoint frameTime = scheduler::SchedulerClock::now(); + return commit(frameTime, kVsyncId); } - void commitAndComposite(const nsecs_t frameTime, const int64_t vsyncId, - const nsecs_t expectedVsyncTime) { - mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), kVsyncId); + void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) { + mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId); } void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); } @@ -386,9 +368,10 @@ public: dispSurface, producer); } - auto commitTransactionsLocked(uint32_t transactionFlags) { + void commitTransactionsLocked(uint32_t transactionFlags) { Mutex::Autolock lock(mFlinger->mStateLock); - return mFlinger->commitTransactionsLocked(transactionFlags); + ftl::FakeGuard guard(kMainThreadContext); + mFlinger->commitTransactionsLocked(transactionFlags); } void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { @@ -418,14 +401,15 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto renderScreenImpl(const RenderArea& renderArea, - SurfaceFlinger::TraverseLayersFunction traverseLayers, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, - bool forSystem, bool regionSampling) { + auto renderScreenImpl(std::unique_ptr<RenderArea> renderArea, + SurfaceFlinger::TraverseLayersFunction traverseLayers, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, + bool forSystem, bool regionSampling) { ScreenCaptureResults captureResults; - return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem, - regionSampling, false /* grayscale */, - captureResults); + return FTL_FAKE_GUARD(kMainThreadContext, + mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers, + buffer, forSystem, regionSampling, + false /* grayscale */, captureResults)); } auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid, @@ -438,106 +422,114 @@ public: return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } - auto& getTransactionQueue() { return mFlinger->mTransactionQueue; } - auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; } - auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; } + auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; } + auto& getPendingTransactionQueue() { + return mFlinger->mTransactionHandler.mPendingTransactionQueues; + } + size_t getPendingTransactionCount() { + return mFlinger->mTransactionHandler.mPendingTransactionCount.load(); + } - auto setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) { + auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo, + Vector<ComposerState>& states, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + std::vector<ListenerCallbacks>& listenerCallbacks, + uint64_t transactionId) { return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, uncacheBuffer, hasListenerCallbacks, listenerCallbacks, transactionId); } - auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); }; + auto setTransactionStateInternal(TransactionState& transaction) { + return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction)); + } + + auto flushTransactionQueues() { + return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId)); + } auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { return mFlinger->onTransact(code, data, reply, flags); } - auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); } + auto getGpuContextPriority() { return mFlinger->getGpuContextPriority(); } auto calculateMaxAcquiredBufferCount(Fps refreshRate, std::chrono::nanoseconds presentLatency) const { return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } - auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, - float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { - return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, - primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, - appRequestRefreshRateMax); + auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { + return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs); } void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) { Mutex::Autolock lock(mFlinger->mStateLock); - mFlinger->onActiveDisplayChangedLocked(activeDisplay); + ftl::FakeGuard guard(kMainThreadContext); + mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay); } - auto createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, - const sp<IBinder>& parentHandle, int32_t* outLayerId, - const sp<Layer>& parentLayer, uint32_t* outTransformHint) { - return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, parentLayer, - outTransformHint); + auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle, + gui::CreateSurfaceResult& outResult) { + args.parentHandle = parentHandle; + return mFlinger->createLayer(args, outResult); } auto mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle, - sp<IBinder>* outHandle, int32_t* outLayerId) { - return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId); + gui::CreateSurfaceResult& outResult) { + return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult); + } + + void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); } + + void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken, + ui::DynamicDisplayInfo* dynamicDisplayInfo) { + mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo); } /* ------------------------------------------------------------------------ * 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& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; } auto& getHwComposer() const { return static_cast<impl::HWComposer&>(mFlinger->getHwComposer()); } auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); } - const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; } - mock::FrameTracer* getFrameTracer() const { return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get()); } - nsecs_t getAnimationTransactionTimeout() const { - return mFlinger->mAnimationTransactionTimeout; - } - /* ------------------------------------------------------------------------ * Read-write access to private data to set up preconditions and assert * post-conditions. */ const auto& displays() const { return mFlinger->mDisplays; } + const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; } const auto& currentState() const { return mFlinger->mCurrentState; } const auto& drawingState() const { return mFlinger->mDrawingState; } const auto& transactionFlags() const { return mFlinger->mTransactionFlags; } + const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; } + const auto& hwcDisplayData() const { return getHwComposer().mDisplayData; } - auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; } + auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } auto& mutableDisplays() { return mFlinger->mDisplays; } + auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; } - auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } - auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; } auto& mutableTexturePool() { return mFlinger->mTexturePool; } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } @@ -546,11 +538,9 @@ public: auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; } - auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; } + auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; } - auto fromHandle(const sp<IBinder>& handle) { - return mFlinger->fromHandle(handle); - } + auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does @@ -560,11 +550,10 @@ public: mutableDisplays().clear(); mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); - mutableInterceptor().clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mFlinger->mCompositionEngine->setRenderEngine( - std::unique_ptr<renderengine::RenderEngine>()); + mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } /* ------------------------------------------------------------------------ @@ -638,7 +627,7 @@ public: return *this; } - auto& setPowerMode(hal::PowerMode mode) { + auto& setPowerMode(std::optional<hal::PowerMode> mode) { mPowerMode = mode; return *this; } @@ -660,16 +649,18 @@ public: // is much longer lived. auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId, mHwcDisplayType); - display->mutableIsConnected() = true; - display->setPowerMode(mPowerMode); + + if (mPowerMode) { + display->setPowerMode(*mPowerMode); + } + flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display); EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _)) .WillRepeatedly( DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}), Return(hal::Error::NONE))); - EXPECT_CALL(*composer, getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _)) .WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()), @@ -728,7 +719,7 @@ public: int32_t mDpiY = DEFAULT_DPI; int32_t mConfigGroup = DEFAULT_CONFIG_GROUP; hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG; - hal::PowerMode mPowerMode = DEFAULT_POWER_MODE; + std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE; const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>* mCapabilities = nullptr; }; @@ -740,16 +731,22 @@ public: std::optional<ui::DisplayConnectionType> connectionType, std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary) : mFlinger(flinger), - mCreationArgs(flinger.mFlinger.get(), flinger.mFlinger->getHwComposer(), - mDisplayToken, display), + mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken, + display), + mConnectionType(connectionType), mHwcDisplayId(hwcDisplayId) { - mCreationArgs.connectionType = connectionType; mCreationArgs.isPrimary = isPrimary; mCreationArgs.initialPowerMode = hal::PowerMode::ON; } sp<IBinder> token() const { return mDisplayToken; } + auto physicalDisplay() const { + return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId()) + .and_then(&PhysicalDisplayId::tryCast) + .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays())); + } + DisplayDeviceState& mutableDrawingDisplayState() { return mFlinger.mutableDrawingState().displays.editValueFor(mDisplayToken); } @@ -770,16 +767,17 @@ public: return mFlinger.mutableDisplays().get(mDisplayToken)->get(); } - // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`. - // Otherwise, it uses `configs`, which the caller must create using the same `modes`. + // If `selectorPtr` is nullptr, the injector creates RefreshRateSelector from the `modes`. + // Otherwise, it uses `selectorPtr`, which the caller must create using the same `modes`. // - // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove - // the `configs` parameter in favor of an alternative setRefreshRateConfigs API. - auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId, - std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) { - mCreationArgs.supportedModes = std::move(modes); + // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateSelector, remove + // the `selectorPtr` parameter in favor of an alternative setRefreshRateSelector API. + auto& setDisplayModes( + DisplayModes modes, DisplayModeId activeModeId, + std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr = nullptr) { + mDisplayModes = std::move(modes); mCreationArgs.activeModeId = activeModeId; - mCreationArgs.refreshRateConfigs = std::move(configs); + mCreationArgs.refreshRateSelector = std::move(selectorPtr); return *this; } @@ -798,7 +796,7 @@ public: return *this; } - auto& setPowerMode(hal::PowerMode mode) { + auto& setPowerMode(std::optional<hal::PowerMode> mode) { mCreationArgs.initialPowerMode = mode; return *this; } @@ -823,10 +821,10 @@ public: sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS { const auto displayId = mCreationArgs.compositionDisplay->getDisplayId(); - auto& modes = mCreationArgs.supportedModes; + auto& modes = mDisplayModes; auto& activeModeId = mCreationArgs.activeModeId; - if (displayId && !mCreationArgs.refreshRateConfigs) { + if (displayId && !mCreationArgs.refreshRateSelector) { if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) { if (modes.empty()) { constexpr DisplayModeId kModeId{0}; @@ -846,57 +844,68 @@ public: activeModeId = kModeId; } - mCreationArgs.refreshRateConfigs = - std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId); + mCreationArgs.refreshRateSelector = + std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId); } } + sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs); + mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display); + DisplayDeviceState state; - if (const auto type = mCreationArgs.connectionType) { + state.isSecure = mCreationArgs.isSecure; + + if (mConnectionType) { LOG_ALWAYS_FATAL_IF(!displayId); - const auto physicalId = PhysicalDisplayId::tryCast(*displayId); - LOG_ALWAYS_FATAL_IF(!physicalId); + const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId); + LOG_ALWAYS_FATAL_IF(!physicalIdOpt); + const auto physicalId = *physicalIdOpt; + + if (mCreationArgs.isPrimary) { + mFlinger.mutableActiveDisplayId() = physicalId; + } + LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); const auto activeMode = modes.get(activeModeId); LOG_ALWAYS_FATAL_IF(!activeMode); + const auto fps = activeMode->get()->getFps(); - state.physical = {.id = *physicalId, - .type = *type, + state.physical = {.id = physicalId, .hwcDisplayId = *mHwcDisplayId, - .deviceProductInfo = {}, - .supportedModes = modes, .activeMode = activeMode->get()}; - } - state.isSecure = mCreationArgs.isSecure; + mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken, + physicalId, *mConnectionType, + std::move(modes), + ui::ColorModes(), + std::nullopt); - sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs); - if (!display->isVirtual()) { - display->setActiveMode(activeModeId); + if (mFlinger.scheduler()) { + mFlinger.scheduler()->registerDisplay(physicalId, + display->holdRefreshRateSelector()); + } + + display->setActiveMode(activeModeId, fps, fps); } - mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display); mFlinger.mutableCurrentState().displays.add(mDisplayToken, state); mFlinger.mutableDrawingState().displays.add(mDisplayToken, state); - if (const auto& physical = state.physical) { - mFlinger.mutablePhysicalDisplayTokens().emplace_or_replace(physical->id, - mDisplayToken); - } - return display; } private: TestableSurfaceFlinger& mFlinger; - sp<BBinder> mDisplayToken = new BBinder(); + sp<BBinder> mDisplayToken = sp<BBinder>::make(); DisplayDeviceCreationArgs mCreationArgs; + DisplayModes mDisplayModes; + const std::optional<ui::DisplayConnectionType> mConnectionType; const std::optional<hal::HWDisplayId> mHwcDisplayId; }; private: - constexpr static int64_t kVsyncId = 123; + static constexpr VsyncId kVsyncId{123}; surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger; diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 6ffc0396d7..a9ae1d3da3 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -44,11 +44,14 @@ namespace android { namespace { using testing::_; +using testing::AllOf; using testing::AnyNumber; using testing::Contains; +using testing::ElementsAre; using testing::HasSubstr; using testing::InSequence; using testing::Not; +using testing::Property; using testing::SizeIs; using testing::StrEq; using testing::UnorderedElementsAre; @@ -645,7 +648,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); ASSERT_EQ(1, globalProto.stats_size()); - const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0); + const SFTimeStatsLayerProto& layerProto = globalProto.stats(0); ASSERT_TRUE(layerProto.has_layer_name()); EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name()); ASSERT_TRUE(layerProto.has_total_frames()); @@ -653,7 +656,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_EQ(6, layerProto.deltas_size()); for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) { ASSERT_EQ(1, deltaProto.histograms_size()); - const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0); + const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0); EXPECT_EQ(1, histogramProto.frame_count()); if ("post2acquire" == deltaProto.delta_name()) { EXPECT_EQ(1, histogramProto.time_millis()); @@ -673,6 +676,46 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { } } +using LayerProto = SFTimeStatsLayerProto; +using DeltaProto = SFTimeStatsDeltaProto; +using BucketProto = SFTimeStatsHistogramBucketProto; + +TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) { + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta + // Slightly unstable frames + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta + + SFTimeStatsGlobalProto globalProto; + ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); + + EXPECT_THAT(globalProto.stats(), + ElementsAre(AllOf( + Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)), + Property(&LayerProto::total_frames, 4), + Property(&LayerProto::deltas, + Contains(AllOf(Property(&DeltaProto::delta_name, + "present2presentDelta"), + Property(&DeltaProto::histograms, + UnorderedElementsAre( + AllOf(Property(&BucketProto:: + time_millis, + 0), + Property(&BucketProto:: + frame_count, + 1)), + AllOf(Property(&BucketProto:: + time_millis, + 1), + Property(&BucketProto:: + frame_count, + 2)))))))))); +} + TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); @@ -1099,8 +1142,10 @@ TEST_F(TimeStatsTest, globalStatsCallback) { kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); @@ -1234,8 +1279,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); @@ -1320,16 +1367,19 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); // The first time record is never uploaded to stats. - ASSERT_EQ(atomList.atom_size(), 3); + ASSERT_EQ(atomList.atom_size(), 4); // Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to - // be: 0 - Battery 1 - Performance 2 - Standard + // be: 0 - Battery 1 - Custom 2 - Performance 3 - Standard const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0); EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0)); @@ -1364,7 +1414,7 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { EXPECT_EQ(atom1.uid(), UID_0); EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0); - EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE); + EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM); const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2); @@ -1377,12 +1427,30 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1}))); EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1}))); - EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES); - EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES); + EXPECT_EQ(atom2.late_acquire_frames(), 0); + EXPECT_EQ(atom2.bad_desired_present_frames(), 0); EXPECT_EQ(atom2.uid(), UID_0); EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0); - EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD); + EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE); + + const SurfaceflingerStatsLayerInfo& atom3 = atomList.atom(3); + + EXPECT_EQ(atom3.layer_name(), genLayerName(LAYER_ID_0)); + EXPECT_EQ(atom3.total_frames(), 1); + EXPECT_EQ(atom3.dropped_frames(), 0); + EXPECT_THAT(atom3.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_THAT(atom3.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1}))); + EXPECT_THAT(atom3.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1}))); + EXPECT_THAT(atom3.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1}))); + EXPECT_THAT(atom3.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_THAT(atom3.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_EQ(atom3.late_acquire_frames(), LATE_ACQUIRE_FRAMES); + EXPECT_EQ(atom3.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES); + EXPECT_EQ(atom3.uid(), UID_0); + EXPECT_EQ(atom3.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); + EXPECT_EQ(atom3.render_rate_bucket(), RENDER_RATE_BUCKET_0); + EXPECT_EQ(atom3.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD); } TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) { @@ -1393,8 +1461,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); @@ -1418,8 +1488,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) { mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); @@ -1437,8 +1509,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); @@ -1457,8 +1531,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000); + std::vector<uint8_t> pulledBytes; + EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes)); std::string pulledData; - EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); + pulledData.assign(pulledBytes.begin(), pulledBytes.end()); SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index ded75318ed..d84698f279 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ - #undef LOG_TAG #define LOG_TAG "CompositionTest" @@ -22,12 +21,18 @@ #include <compositionengine/mock/DisplaySurface.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <gui/LayerState.h> #include <gui/SurfaceComposerClient.h> +#include <gui/fake/BufferData.h> #include <log/log.h> #include <ui/MockFence.h> #include <utils/String8.h> +#include <vector> +#include <binder/Binder.h> +#include "FrontEnd/TransactionHandler.h" #include "TestableSurfaceFlinger.h" +#include "TransactionState.h" #include "mock/MockEventThread.h" #include "mock/MockVsyncController.h" @@ -37,7 +42,9 @@ using testing::_; using testing::Return; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using frontend::TransactionHandler; +constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5); class TransactionApplicationTest : public testing::Test { public: TransactionApplicationTest() { @@ -60,13 +67,15 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); EXPECT_CALL(*mVSyncTracker, currentPeriod()) @@ -76,6 +85,7 @@ public: mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController), std::unique_ptr<mock::VSyncTracker>(mVSyncTracker), std::move(eventThread), std::move(sfEventThread)); + mFlinger.flinger()->addTransactionReadyFilters(); } TestableSurfaceFlinger mFlinger; @@ -107,22 +117,20 @@ public: EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime); } - void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows, - int64_t desiredPresentTime, bool isAutoTimestamp, - const FrameTimelineInfo& frameTimelineInfo) { + void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime, + bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) { mTransactionNumber++; - transaction.flags |= flags; // ISurfaceComposer::eSynchronous; - transaction.inputWindowCommands.syncInputWindows = syncInputWindows; + transaction.flags |= flags; transaction.desiredPresentTime = desiredPresentTime; transaction.isAutoTimestamp = isAutoTimestamp; transaction.frameTimelineInfo = frameTimelineInfo; } - void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + void NotPlacedOnTransactionQueue(uint32_t flags) { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); TransactionInfo transaction; - setupSingle(transaction, flags, syncInputWindows, + setupSingle(transaction, flags, /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, FrameTimelineInfo{}); nsecs_t applicationTime = systemTime(); @@ -133,31 +141,25 @@ public: transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks, transaction.id); - // If transaction is synchronous or syncs input windows, SF - // applyTransactionState should time out (5s) wating for SF to commit - // the transaction or to receive a signal that syncInputWindows has - // completed. If this is animation, it should not time out waiting. + // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for + // SF to commit the transaction. If this is animation, it should not time out waiting. nsecs_t returnedTime = systemTime(); - if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) { - EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout()); - } else { - EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout()); - } + EXPECT_LE(returnedTime, applicationTime + TRANSACTION_TIMEOUT); // Each transaction should have been placed on the transaction queue - auto transactionQueue = mFlinger.getTransactionQueue(); - EXPECT_EQ(1u, transactionQueue.size()); + auto& transactionQueue = mFlinger.getTransactionQueue(); + EXPECT_FALSE(transactionQueue.isEmpty()); } - void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + void PlaceOnTransactionQueue(uint32_t flags) { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); // first check will see desired present time has not passed, // but afterwards it will look like the desired present time has passed nsecs_t time = systemTime(); TransactionInfo transaction; - setupSingle(transaction, flags, syncInputWindows, - /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{}); + setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false, + FrameTimelineInfo{}); nsecs_t applicationSentTime = systemTime(); mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, transaction.displays, transaction.flags, @@ -167,37 +169,27 @@ public: transaction.id); nsecs_t returnedTime = systemTime(); - if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) { - EXPECT_GE(systemTime(), - applicationSentTime + mFlinger.getAnimationTransactionTimeout()); - } else { - EXPECT_LE(returnedTime, - applicationSentTime + mFlinger.getAnimationTransactionTimeout()); - } + EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT); // This transaction should have been placed on the transaction queue - auto transactionQueue = mFlinger.getTransactionQueue(); - EXPECT_EQ(1u, transactionQueue.size()); + auto& transactionQueue = mFlinger.getTransactionQueue(); + EXPECT_FALSE(transactionQueue.isEmpty()); } - void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); + void BlockedByPriorTransaction(uint32_t flags) { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); nsecs_t time = systemTime(); - if (!syncInputWindows) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2); - } else { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - } + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2); + // transaction that should go on the pending thread TransactionInfo transactionA; - setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, - /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{}); + setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ time + s2ns(1), false, + FrameTimelineInfo{}); // transaction that would not have gone on the pending thread if not // blocked TransactionInfo transactionB; - setupSingle(transactionB, flags, syncInputWindows, - /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, - FrameTimelineInfo{}); + setupSingle(transactionB, flags, /*desiredPresentTime*/ systemTime(), + /*isAutoTimestamp*/ true, FrameTimelineInfo{}); nsecs_t applicationSentTime = systemTime(); mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, @@ -210,7 +202,7 @@ public: // This thread should not have been blocked by the above transaction // (5s is the timeout period that applyTransactionState waits for SF to // commit the transaction) - EXPECT_LE(systemTime(), applicationSentTime + mFlinger.getAnimationTransactionTimeout()); + EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT); // transaction that would goes to pending transaciton queue. mFlinger.flushTransactionQueues(); @@ -226,14 +218,7 @@ public: // if this is an animation, this thread should be blocked for 5s // in setTransactionState waiting for transactionA to flush. Otherwise, // the transaction should be placed on the pending queue - if (flags & (ISurfaceComposer::eSynchronous) || - syncInputWindows) { - EXPECT_GE(systemTime(), - applicationSentTime + mFlinger.getAnimationTransactionTimeout()); - } else { - EXPECT_LE(systemTime(), - applicationSentTime + mFlinger.getAnimationTransactionTimeout()); - } + EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT); // transaction that would goes to pending transaciton queue. mFlinger.flushTransactionQueues(); @@ -248,13 +233,13 @@ public: int mTransactionNumber = 0; }; -TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { - ASSERT_EQ(0u, mFlinger.getTransactionQueue().size()); +TEST_F(TransactionApplicationTest, AddToPendingQueue) { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); TransactionInfo transactionA; // transaction to go on pending queue - setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, - /*desiredPresentTime*/ s2ns(1), false, FrameTimelineInfo{}); + setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false, + FrameTimelineInfo{}); mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, transactionA.displays, transactionA.flags, transactionA.applyToken, transactionA.inputWindowCommands, transactionA.desiredPresentTime, @@ -262,10 +247,27 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { mHasListenerCallbacks, mCallbacks, transactionA.id); auto& transactionQueue = mFlinger.getTransactionQueue(); - ASSERT_EQ(1u, transactionQueue.size()); + ASSERT_FALSE(transactionQueue.isEmpty()); - auto& transactionState = transactionQueue.front(); + auto transactionState = transactionQueue.pop().value(); checkEqual(transactionA, transactionState); +} + +TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + + TransactionInfo transactionA; // transaction to go on pending queue + setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false, + FrameTimelineInfo{}); + mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states, + transactionA.displays, transactionA.flags, transactionA.applyToken, + transactionA.inputWindowCommands, transactionA.desiredPresentTime, + transactionA.isAutoTimestamp, transactionA.uncacheBuffer, + mHasListenerCallbacks, mCallbacks, transactionA.id); + + auto& transactionQueue = mFlinger.getTransactionQueue(); + ASSERT_FALSE(transactionQueue.isEmpty()); // because flushing uses the cached expected present time, we send an empty // transaction here (sending a null applyToken to fake it as from a @@ -281,41 +283,21 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { // passed mFlinger.flushTransactionQueues(); - EXPECT_EQ(0u, transactionQueue.size()); -} - -TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) { - NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); + EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); } TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) { - NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true); -} - -TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) { - PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); + NotPlacedOnTransactionQueue(/*flags*/ 0); } TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) { - PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true); -} - -TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) { - BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); -} - -TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) { - BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); -} - -TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) { - BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true); + PlaceOnTransactionQueue(/*flags*/ 0); } TEST_F(TransactionApplicationTest, FromHandle) { sp<IBinder> badHandle; auto ret = mFlinger.fromHandle(badHandle); - EXPECT_EQ(nullptr, ret.promote().get()); + EXPECT_EQ(nullptr, ret.get()); } class LatchUnsignaledTest : public TransactionApplicationTest { @@ -323,9 +305,10 @@ public: void TearDown() override { // Clear all transaction queues to release all transactions we sent // in the tests. Otherwise, gmock complains about memory leaks. - mFlinger.getTransactionQueue().clear(); + while (!mFlinger.getTransactionQueue().isEmpty()) { + mFlinger.getTransactionQueue().pop(); + } mFlinger.getPendingTransactionQueue().clear(); - mFlinger.getTransactionCommittedSignals().clear(); mFlinger.commitTransactionsLocked(eTransactionMask); mFlinger.mutableCurrentState().layersSortedByZ.clear(); mFlinger.mutableDrawingState().layersSortedByZ.clear(); @@ -339,12 +322,14 @@ public: ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) { ComposerState state; - state.state.bufferData = std::make_shared<BufferData>(); + state.state.bufferData = + std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1, + /* height */ 2, /* pixelFormat */ 0, + /* outUsage */ 0); state.state.bufferData->acquireFence = std::move(fence); state.state.layerId = layerId; state.state.surface = - sp<BufferStateLayer>::make( - LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {})) + sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {})) ->getHandle(); state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged; @@ -358,14 +343,12 @@ public: TransactionInfo createTransactionInfo(const sp<IBinder>& applyToken, const std::vector<ComposerState>& states) { TransactionInfo transaction; - const uint32_t kFlags = ISurfaceComposer::eSynchronous; - const bool kSyncInputWindows = false; + const uint32_t kFlags = 0; const nsecs_t kDesiredPresentTime = systemTime(); const bool kIsAutoTimestamp = true; const auto kFrameTimelineInfo = FrameTimelineInfo{}; - setupSingle(transaction, kFlags, kSyncInputWindows, kDesiredPresentTime, kIsAutoTimestamp, - kFrameTimelineInfo); + setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo); transaction.applyToken = applyToken; for (const auto& state : states) { transaction.states.push_back(state); @@ -375,23 +358,31 @@ public: } void setTransactionStates(const std::vector<TransactionInfo>& transactions, - size_t expectedTransactionsApplied, size_t expectedTransactionsPending) { - EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); + EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); - for (const auto& transaction : transactions) { - mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, - transaction.displays, transaction.flags, - transaction.applyToken, transaction.inputWindowCommands, - transaction.desiredPresentTime, - transaction.isAutoTimestamp, transaction.uncacheBuffer, - mHasListenerCallbacks, mCallbacks, transaction.id); + for (auto transaction : transactions) { + std::vector<ResolvedComposerState> resolvedStates; + resolvedStates.reserve(transaction.states.size()); + for (auto& state : transaction.states) { + resolvedStates.emplace_back(std::move(state)); + } + + TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates, + transaction.displays, transaction.flags, + transaction.applyToken, + transaction.inputWindowCommands, + transaction.desiredPresentTime, + transaction.isAutoTimestamp, + transaction.uncacheBuffer, systemTime(), 0, + mHasListenerCallbacks, mCallbacks, getpid(), + static_cast<int>(getuid()), transaction.id); + mFlinger.setTransactionStateInternal(transactionState); } mFlinger.flushTransactionQueues(); - EXPECT_EQ(0u, mFlinger.getTransactionQueue().size()); - EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size()); - EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size()); + EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); + EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount()); } }; @@ -407,22 +398,19 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleSignaledFromTheQue const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = createTransactionInfo(kApplyToken, {createComposerState(kLayerId, fence(Fence::Status::Signaled), layer_state_t::eBufferChanged)}); - setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleUnSignaledFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto unsignaledTransaction = @@ -432,15 +420,13 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleUnSignaledFromTheQ fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -448,17 +434,17 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBu { createComposerState(kLayerId, fence(Fence::Status::Unsignaled), - layer_state_t::eCropChanged), + layer_state_t::eCropChanged | + layer_state_t:: + eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -470,15 +456,13 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBu layer_state_t:: eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueueSameApplyTokenMultiState) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto mixedTransaction = @@ -491,8 +475,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueueSameApplyTokenMu fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTransaction) { @@ -500,7 +483,6 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTr IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto mixedTransaction = @@ -513,8 +495,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTr fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) { @@ -522,7 +503,6 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) { IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = @@ -539,8 +519,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) { fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, @@ -551,7 +530,6 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, const sp<IBinder> kApplyToken3 = sp<BBinder>::make(); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -578,43 +556,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, }); setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2}, - kExpectedTransactionsApplied, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) { - const sp<IBinder> kApplyToken1 = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); - const sp<IBinder> kApplyToken3 = sp<BBinder>::make(); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; - const auto kExpectedTransactionsPending = 1u; - - const auto signaledTransaction = - createTransactionInfo(kApplyToken1, - { - createComposerState(kLayerId1, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - const auto signaledTransaction2 = - createTransactionInfo(kApplyToken2, - { - createComposerState(kLayerId1, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken3, - { - createComposerState(kLayerId2, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - - setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction}, - kExpectedTransactionsApplied, kExpectedTransactionsPending); + kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) { @@ -622,7 +564,6 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameA IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -639,7 +580,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameA fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied, + setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsPending); } @@ -649,7 +590,6 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueue) { const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -667,14 +607,13 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueue) { layer_state_t::eBufferChanged), }); setTransactionStates({unsignaledTransaction, unsignaledTransaction2}, - kExpectedTransactionsApplied, kExpectedTransactionsPending); + kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -688,8 +627,7 @@ TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) { // Get VsyncModulator out of the default config static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated()); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } class LatchUnsignaledDisabledTest : public LatchUnsignaledTest { @@ -704,22 +642,19 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = createTransactionInfo(kApplyToken, {createComposerState(kLayerId, fence(Fence::Status::Signaled), layer_state_t::eBufferChanged)}); - setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -729,15 +664,13 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueue) { fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueSameLayerId) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -750,8 +683,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueSameLayerId) { fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) { @@ -759,7 +691,6 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) { IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 0u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -772,8 +703,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) { fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) { @@ -781,7 +711,6 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLa IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = @@ -798,8 +727,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLa fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) { @@ -808,7 +736,6 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) { const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 1u; const auto unsignaledTransaction = @@ -825,7 +752,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) { fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied, + setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsPending); } @@ -834,7 +761,6 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueSameApplyToken) { IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 1u; const auto signaledTransaction = @@ -851,7 +777,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueSameApplyToken) { fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied, + setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsPending); } @@ -860,8 +786,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheUnsignaledTheQueue) { IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 0u; - const auto kExpectedTransactionsPending = 1u; + const auto kExpectedTransactionsPending = 2u; const auto unsignaledTransaction = createTransactionInfo(kApplyToken, @@ -878,7 +803,7 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheUnsignaledTheQueue) { layer_state_t::eBufferChanged), }); setTransactionStates({unsignaledTransaction, unsignaledTransaction2}, - kExpectedTransactionsApplied, kExpectedTransactionsPending); + kExpectedTransactionsPending); } class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest { @@ -893,37 +818,32 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = createTransactionInfo(kApplyToken, {createComposerState(kLayerId, fence(Fence::Status::Signaled), layer_state_t::eBufferChanged)}); - setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto unsignaledTransaction = createTransactionInfo(kApplyToken, {createComposerState(kLayerId, fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged)}); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto mixedTransaction = @@ -932,8 +852,7 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) { layer_state_t::eBufferChanged), createComposerState(kLayerId, fence(Fence::Status::Signaled), layer_state_t::eBufferChanged)}); - setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) { @@ -941,7 +860,6 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) { IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto mixedTransaction = @@ -950,8 +868,7 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) { layer_state_t::eBufferChanged), createComposerState(kLayerId2, fence(Fence::Status::Signaled), layer_state_t::eBufferChanged)}); - setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) { @@ -959,7 +876,6 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLaye IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = @@ -976,8 +892,7 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLaye fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) { @@ -986,7 +901,6 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = @@ -1003,7 +917,7 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) fence(Fence::Status::Unsignaled), layer_state_t::eBufferChanged), }); - setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied, + setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsPending); } @@ -1012,7 +926,6 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyTo IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto unsignaledTransaction = @@ -1029,7 +942,7 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyTo fence(Fence::Status::Signaled), layer_state_t::eBufferChanged), }); - setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied, + setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsPending); } @@ -1039,7 +952,6 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) { const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); const auto kLayerId1 = 1; const auto kLayerId2 = 2; - const auto kExpectedTransactionsApplied = 2u; const auto kExpectedTransactionsPending = 0u; const auto unsignaledTransaction = @@ -1057,14 +969,13 @@ TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) { layer_state_t::eBufferChanged), }); setTransactionStates({unsignaledTransaction, unsignaledTransaction2}, - kExpectedTransactionsApplied, kExpectedTransactionsPending); + kExpectedTransactionsPending); } TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); const auto kLayerId = 1; - const auto kExpectedTransactionsApplied = 1u; const auto kExpectedTransactionsPending = 0u; const auto unsignaledTransaction = @@ -1078,8 +989,19 @@ TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) { // Get VsyncModulator out of the default config static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated()); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied, - kExpectedTransactionsPending); + setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); +} + +TEST(TransactionHandlerTest, QueueTransaction) { + TransactionHandler handler; + TransactionState transaction; + transaction.applyToken = sp<BBinder>::make(); + transaction.id = 42; + handler.queueTransaction(std::move(transaction)); + std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions(); + + EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u); + EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u); } } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 5364630400..1173d1c876 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -56,11 +56,11 @@ public: ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - sp<BufferStateLayer> createBufferStateLayer() { + sp<Layer> createLayer() { sp<Client> client; LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, LayerMetadata()); - return new BufferStateLayer(args); + return sp<Layer>::make(args); } void commitTransaction(Layer* layer) { @@ -74,13 +74,15 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -99,9 +101,9 @@ public: FenceToFenceTimeMap fenceFactory; void BLASTTransactionSendsFrameTracerEvents() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); - sp<Fence> fence(new Fence()); + sp<Fence> fence(sp<Fence>::make()); int32_t layerId = layer->getSequence(); uint64_t bufferId = 42; uint64_t frameNumber = 5; @@ -127,7 +129,6 @@ public: dequeueTime, FrameTimelineInfo{}); commitTransaction(layer.get()); - bool computeVisisbleRegions; nsecs_t latchTime = 25; EXPECT_CALL(*mFlinger.getFrameTracer(), traceFence(layerId, bufferId, frameNumber, _, @@ -135,7 +136,7 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceTimestamp(layerId, bufferId, frameNumber, latchTime, FrameTracer::FrameEvent::LATCH, /*duration*/ 0)); - layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0); + layer->updateTexImage(latchTime); auto glDoneFence = fenceFactory.createFenceTimeForTest(fence); auto presentFence = fenceFactory.createFenceTimeForTest(fence); diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp index f5e3b77ca6..b6427c0ffb 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -27,8 +27,8 @@ using namespace android::surfaceflinger; namespace android { TEST(TransactionProtoParserTest, parse) { - const sp<IBinder> layerHandle = new BBinder(); - const sp<IBinder> displayHandle = new BBinder(); + const sp<IBinder> layerHandle = sp<BBinder>::make(); + const sp<IBinder> displayHandle = sp<BBinder>::make(); TransactionState t1; t1.originPid = 1; t1.originUid = 2; @@ -46,14 +46,14 @@ TEST(TransactionProtoParserTest, parse) { size_t layerCount = 2; t1.states.reserve(layerCount); for (uint32_t i = 0; i < layerCount; i++) { - ComposerState s; + ResolvedComposerState s; if (i == 1) { layer.parentSurfaceControlForChild = - new SurfaceControl(SurfaceComposerClient::getDefault(), layerHandle, nullptr, - 42); + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42, + "#42"); } s.state = layer; - t1.states.add(s); + t1.states.emplace_back(s); } size_t displayCount = 2; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 5bb4c92a8e..ae03db43a7 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -56,11 +56,10 @@ public: ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - sp<BufferStateLayer> createBufferStateLayer() { + sp<Layer> createLayer() { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0, - LayerMetadata()); - return new BufferStateLayer(args); + LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata()); + return sp<Layer>::make(args); } void commitTransaction(Layer* layer) { @@ -74,13 +73,15 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); @@ -99,9 +100,11 @@ public: FenceToFenceTimeMap fenceFactory; void PresentedSurfaceFrameForBufferlessTransaction() { - sp<BufferStateLayer> layer = createBufferStateLayer(); - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0}, - 10); + sp<Layer> layer = createLayer(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_TRUE(layer->mDrawingState.bufferSurfaceFrameTX == nullptr); const auto surfaceFrame = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1); @@ -112,8 +115,8 @@ public: } void PresentedSurfaceFrameForBufferTransaction() { - sp<BufferStateLayer> layer = createBufferStateLayer(); - sp<Fence> fence(new Fence()); + sp<Layer> layer = createLayer(); + sp<Fence> fence(sp<Fence>::make()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); BufferData bufferData; bufferData.acquireFence = fence; @@ -125,8 +128,10 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -136,8 +141,7 @@ public: // Buffers are presented only at latch time. EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); EXPECT_EQ(1, surfaceFrame->getToken()); EXPECT_EQ(true, surfaceFrame->getIsBuffer()); @@ -145,9 +149,9 @@ public: } void DroppedSurfaceFrameForBufferTransaction() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); - sp<Fence> fence1(new Fence()); + sp<Fence> fence1(sp<Fence>::make()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); BufferData bufferData; bufferData.acquireFence = fence1; @@ -159,13 +163,15 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; - sp<Fence> fence2(new Fence()); + sp<Fence> fence2(sp<Fence>::make()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); nsecs_t start = systemTime(); bufferData.acquireFence = fence2; @@ -177,8 +183,7 @@ public: 2ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -187,8 +192,7 @@ public: const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; commitTransaction(layer.get()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); EXPECT_EQ(1, droppedSurfaceFrame->getToken()); EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer()); @@ -203,15 +207,17 @@ public: } void BufferlessSurfaceFramePromotedToBufferSurfaceFrame() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0}, - 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); - sp<Fence> fence(new Fence()); + sp<Fence> fence(sp<Fence>::make()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); BufferData bufferData; bufferData.acquireFence = fence; @@ -223,8 +229,7 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -237,15 +242,14 @@ public: // Buffers are presented only at latch time. EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState()); } void BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists() { - sp<BufferStateLayer> layer = createBufferStateLayer(); - sp<Fence> fence(new Fence()); + sp<Layer> layer = createLayer(); + sp<Fence> fence(sp<Fence>::make()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); BufferData bufferData; bufferData.acquireFence = fence; @@ -257,33 +261,38 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0}, - 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); } void MultipleSurfaceFramesPresentedTogether() { - sp<BufferStateLayer> layer = createBufferStateLayer(); - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0}, - 10); + sp<Layer> layer = createLayer(); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferlessSurfaceFrame1 = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1); - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 4, /*inputEventId*/ 0}, - 10); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = 4; + ftInfo2.inputEventId = 0; + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferlessSurfaceFrame2 = layer->mDrawingState.bufferlessSurfaceFramesTX[4]; - sp<Fence> fence(new Fence()); + sp<Fence> fence(sp<Fence>::make()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); BufferData bufferData; bufferData.acquireFence = fence; @@ -295,8 +304,10 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 3, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo3; + ftInfo3.vsyncId = 3; + ftInfo3.inputEventId = 0; + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -318,16 +329,15 @@ public: // Buffers are presented only at latch time. EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState()); } void PendingSurfaceFramesRemovedAfterClassification() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); - sp<Fence> fence1(new Fence()); + sp<Fence> fence1(sp<Fence>::make()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); BufferData bufferData; bufferData.acquireFence = fence1; @@ -339,12 +349,14 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; - sp<Fence> fence2(new Fence()); + sp<Fence> fence2(sp<Fence>::make()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); bufferData.acquireFence = fence2; bufferData.frameNumber = 1; @@ -355,16 +367,14 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; commitTransaction(layer.get()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in // pendingJankClassifications. @@ -377,9 +387,9 @@ public: } void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); - sp<Fence> fence1(new Fence()); + sp<Fence> fence1(sp<Fence>::make()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); BufferData bufferData; bufferData.acquireFence = fence1; @@ -391,13 +401,15 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; - sp<Fence> fence2(new Fence()); + sp<Fence> fence2(sp<Fence>::make()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); auto dropStartTime1 = systemTime(); bufferData.acquireFence = fence2; @@ -409,14 +421,16 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfoInv; + ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID; + ftInfoInv.inputEventId = 0; + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame2 = layer->mDrawingState.bufferSurfaceFrameTX; - sp<Fence> fence3(new Fence()); + sp<Fence> fence3(sp<Fence>::make()); auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3); auto dropStartTime2 = systemTime(); bufferData.acquireFence = fence3; @@ -428,8 +442,10 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 2, /*inputEventId*/ 0}); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = 2; + ftInfo2.inputEventId = 0; + layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -438,8 +454,7 @@ public: const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; commitTransaction(layer.get()); - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); EXPECT_EQ(1, droppedSurfaceFrame1->getToken()); EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer()); @@ -461,11 +476,11 @@ public: } void MultipleCommitsBeforeLatch() { - sp<BufferStateLayer> layer = createBufferStateLayer(); + sp<Layer> layer = createLayer(); uint32_t surfaceFramesPendingClassification = 0; std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames; for (int i = 0; i < 10; i += 2) { - sp<Fence> fence(new Fence()); + sp<Fence> fence(sp<Fence>::make()); BufferData bufferData; bufferData.acquireFence = fence; bufferData.frameNumber = 1; @@ -476,11 +491,14 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}); - layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, - /*inputEventId*/ 0}, - 10); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = 1; + ftInfo.inputEventId = 0; + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); + FrameTimelineInfo ftInfo2; + ftInfo2.vsyncId = 2; + ftInfo2.inputEventId = 0; + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); auto& bufferlessSurfaceFrame = @@ -494,8 +512,7 @@ public: } auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; - bool computeVisisbleRegions; - layer->updateTexImage(computeVisisbleRegions, 15, 0); + layer->updateTexImage(15); // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame. // Since we don't have access to DisplayFrame here, trigger an onPresent directly. for (auto& surfaceFrame : bufferlessSurfaceFrames) { diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 61b72a0b9b..482c3a8e50 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -101,10 +101,10 @@ protected: void SetUp() override { // add layers mTracing.setBufferSize(SMALL_BUFFER_SIZE); - const sp<IBinder> fakeLayerHandle = new BBinder(); + const sp<IBinder> fakeLayerHandle = sp<BBinder>::make(); mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent", 123 /* flags */, -1 /* parentId */); - const sp<IBinder> fakeChildLayerHandle = new BBinder(); + const sp<IBinder> fakeChildLayerHandle = sp<BBinder>::make(); mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child", 456 /* flags */, mParentLayerId); @@ -112,21 +112,23 @@ protected: { TransactionState transaction; transaction.id = 50; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged; layerState.state.z = 42; - transaction.states.add(layerState); - ComposerState childState; + transaction.states.emplace_back(layerState); + ResolvedComposerState childState; childState.state.surface = fakeChildLayerHandle; childState.state.what = layer_state_t::eLayerChanged; childState.state.z = 43; - transaction.states.add(childState); + transaction.states.emplace_back(childState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; transactions.emplace_back(transaction); VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId; + mTracing.onLayerAddedToDrawingState(mParentLayerId, VSYNC_ID_FIRST_LAYER_CHANGE); + mTracing.onLayerAddedToDrawingState(mChildLayerId, VSYNC_ID_FIRST_LAYER_CHANGE); mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE); flush(VSYNC_ID_FIRST_LAYER_CHANGE); } @@ -136,12 +138,12 @@ protected: { TransactionState transaction; transaction.id = 51; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged; layerState.state.z = 41; layerState.state.x = 22; - transaction.states.add(layerState); + transaction.states.emplace_back(layerState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; @@ -232,32 +234,34 @@ protected: void SetUp() override { // add layers mTracing.setBufferSize(SMALL_BUFFER_SIZE); - const sp<IBinder> fakeLayerHandle = new BBinder(); + const sp<IBinder> fakeLayerHandle = sp<BBinder>::make(); mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer", 123 /* flags */, -1 /* parentId */); - const sp<IBinder> fakeMirrorLayerHandle = new BBinder(); + const sp<IBinder> fakeMirrorLayerHandle = sp<BBinder>::make(); mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror", mLayerId); + mTracing.onLayerAddedToDrawingState(mLayerId, mVsyncId); + mTracing.onLayerAddedToDrawingState(mMirrorLayerId, mVsyncId); // add some layer transaction { TransactionState transaction; transaction.id = 50; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged; layerState.state.z = 42; - transaction.states.add(layerState); - ComposerState mirrorState; + transaction.states.emplace_back(layerState); + ResolvedComposerState mirrorState; mirrorState.state.surface = fakeMirrorLayerHandle; mirrorState.state.what = layer_state_t::eLayerChanged; mirrorState.state.z = 43; - transaction.states.add(mirrorState); + transaction.states.emplace_back(mirrorState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; transactions.emplace_back(transaction); - mTracing.addCommittedTransactions(transactions, ++mVsyncId); + mTracing.addCommittedTransactions(transactions, mVsyncId); flush(mVsyncId); } } diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index 15fea9c6b2..da87f1db17 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -22,7 +22,6 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> -#include "BufferStateLayer.h" #include "TestableSurfaceFlinger.h" #include "TunnelModeEnabledReporter.h" #include "mock/DisplayHardware/MockComposer.h" @@ -64,7 +63,7 @@ protected: void setupScheduler(); void setupComposer(uint32_t virtualDisplayCount); - sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata); + sp<Layer> createBufferStateLayer(LayerMetadata metadata); TestableSurfaceFlinger mFlinger; Hwc2::mock::Composer* mComposer = nullptr; @@ -95,11 +94,10 @@ TunnelModeEnabledReporterTest::~TunnelModeEnabledReporterTest() { mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); } -sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer( - LayerMetadata metadata = {}) { +sp<Layer> TunnelModeEnabledReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { sp<Client> client; LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); - return new BufferStateLayer(args); + return sp<Layer>::make(args); } void TunnelModeEnabledReporterTest::setupScheduler() { @@ -108,13 +106,15 @@ void TunnelModeEnabledReporterTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); + .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), + mock::EventThread::kCallingUid, + ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 2da266bd33..47c2deef51 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -54,6 +54,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } + void setDivisor(unsigned) final {} void dump(std::string&) const final {} private: @@ -91,6 +92,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } + void setDivisor(unsigned) final {} void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index b7f968dc17..2b86e94244 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -44,6 +44,8 @@ public: ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime)); ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); + ON_CALL(*this, currentPeriod()) + .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod)); } MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); @@ -53,6 +55,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -62,6 +65,8 @@ public: return (timePoint - (timePoint % mPeriod) + mPeriod); } + nsecs_t getCurrentPeriod() const { return mPeriod; } + protected: nsecs_t const mPeriod; }; @@ -393,6 +398,43 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { EXPECT_THAT(cb1.mCalls[0], Eq(1063)); } +TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + .Times(4) + .WillOnce(Return(1000)) + .WillOnce(Return(2000)) + .WillOnce(Return(2500)) + .WillOnce(Return(4000)); + + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 3900)).InSequence(seq); + + CountingCallback cb(mDispatch); + + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0}); + + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(1000)); + + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(2)); + EXPECT_THAT(cb.mCalls[1], Eq(2000)); + + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(3)); + EXPECT_THAT(cb.mCalls[2], Eq(4000)); +} + TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) .Times(4) diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 74d2b7d9b6..3095e8aa9a 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -532,6 +532,26 @@ TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) { EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError)); } +TEST_F(VSyncPredictorTest, setDivisorIsRespected) { + auto last = mNow; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod; + last = mNow; + tracker.addVsyncTimestamp(mNow); + } + + tracker.setDivisor(3); + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 30a3f9a4a7..8bd689a61d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -50,6 +50,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; @@ -77,12 +78,12 @@ struct MockVSyncDispatch : VSyncDispatch { }; std::shared_ptr<android::FenceTime> generateInvalidFence() { - sp<Fence> fence = new Fence(); + sp<Fence> fence = sp<Fence>::make(); return std::make_shared<android::FenceTime>(fence); } std::shared_ptr<android::FenceTime> generatePendingFence() { - sp<Fence> fence = new Fence(dup(fileno(tmpfile()))); + sp<Fence> fence = sp<Fence>::make(dup(fileno(tmpfile()))); return std::make_shared<android::FenceTime>(fence); } @@ -92,7 +93,7 @@ void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs } std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) { - sp<Fence> fence = new Fence(dup(fileno(tmpfile()))); + sp<Fence> fence = sp<Fence>::make(dup(fileno(tmpfile()))); std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence); signalFenceWithTime(ft, time); return ft; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h index 657ced3d08..5654691884 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h @@ -17,6 +17,7 @@ #pragma once #include <gmock/gmock.h> +#include <scheduler/Time.h> #include "DisplayHardware/PowerAdvisor.h" @@ -35,15 +36,15 @@ public: MockAidlPowerHalWrapper(); ~MockAidlPowerHalWrapper() override; MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override)); - MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(bool, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); MOCK_METHOD(void, restartPowerHintSession, (), (override)); MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds), (override)); MOCK_METHOD(bool, startPowerHintSession, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp), + MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (Duration actualDuration, TimePoint timestamp), (override)); MOCK_METHOD(bool, shouldReconnectHAL, (), (override)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index aa8b5211e9..836e3a47cc 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -32,7 +32,6 @@ using android::hardware::graphics::common::V1_0::Transform; using android::hardware::graphics::common::V1_1::RenderIntent; using android::hardware::graphics::common::V1_2::ColorMode; using android::hardware::graphics::common::V1_2::Dataspace; -using android::hardware::graphics::common::V1_2::Hdr; using android::hardware::graphics::common::V1_2::PixelFormat; using android::hardware::graphics::composer::V2_1::Config; @@ -56,8 +55,8 @@ public: std::vector<aidl::android::hardware::graphics::composer3::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&)); - MOCK_METHOD0(resetCommands, void()); - MOCK_METHOD0(executeCommands, Error()); + MOCK_METHOD1(resetCommands, void(Display)); + MOCK_METHOD1(executeCommands, Error(Display)); MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t()); MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*)); MOCK_METHOD1(destroyVirtualDisplay, Error(Display)); @@ -164,6 +163,10 @@ public: MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds)); MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*)); MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*)); + MOCK_METHOD1(getOverlaySupport, + Error(aidl::android::hardware::graphics::composer3::OverlayProperties*)); + MOCK_METHOD1(onHotplugConnect, void(Display)); + MOCK_METHOD1(onHotplugDisconnect, void(Display)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h index a83ecbca26..c78b6bdc18 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h @@ -33,4 +33,9 @@ inline DisplayModePtr createDisplayMode( .build(); } +inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayModeId modeId, + Fps refreshRate) { + return createDisplayMode(modeId, refreshRate, {}, {}, displayId); +} + } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 07cd15da93..40f59b8cb9 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -105,6 +105,9 @@ public: MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override)); MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *), (const override)); + MOCK_METHOD(hal::Error, getOverlaySupport, + (aidl::android::hardware::graphics::composer3::OverlayProperties *), + (const override)); }; class Layer : public HWC2::Layer { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 439f6f4e75..f4ded216cb 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -23,6 +23,7 @@ using android::binder::Status; using android::hardware::power::IPowerHintSession; +using android::hardware::power::SessionHint; using namespace android::hardware::power; @@ -40,6 +41,8 @@ public: MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override)); MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override)); + MOCK_METHOD(Status, sendHint, (SessionHint), (override)); + MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index aede250db5..7fc625c4b6 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -32,11 +32,11 @@ public: MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected), (override)); MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override)); - MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override)); MOCK_METHOD(void, sendActualWorkDuration, (), (override)); MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); @@ -44,25 +44,24 @@ public: MOCK_METHOD(void, setGpuFenceTime, (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); MOCK_METHOD(void, setHwcValidateTiming, - (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime), (override)); MOCK_METHOD(void, setHwcPresentTiming, - (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); MOCK_METHOD(void, setRequiresClientComposition, (DisplayId displayId, bool requiresClientComposition), (override)); - MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); - MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime), + MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override)); + MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setHwcPresentDelayedTime, - (DisplayId displayId, - std::chrono::steady_clock::time_point earliestFrameStartTime)); - MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); - MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); - MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + (DisplayId displayId, TimePoint earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override)); MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); - MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h new file mode 100644 index 0000000000..a71e82cc75 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/gui/DisplayModeSpecs.h> + +namespace android::mock { + +inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching, + float minFps, float maxFps) { + gui::DisplayModeSpecs specs; + specs.defaultMode = defaultMode; + specs.allowGroupSwitching = allowGroupSwitching; + specs.primaryRanges.physical.min = minFps; + specs.primaryRanges.physical.max = maxFps; + specs.primaryRanges.render = specs.primaryRanges.physical; + specs.appRequestRanges = specs.primaryRanges; + return specs; +} + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index c5ca86a651..f8567bd636 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -24,16 +24,17 @@ namespace android::mock { class EventThread : public android::EventThread { public: + static constexpr auto kCallingUid = static_cast<uid_t>(0); + EventThread(); ~EventThread() override; MOCK_CONST_METHOD2(createEventConnection, - sp<EventThreadConnection>(ResyncCallback, - ISurfaceComposer::EventRegistrationFlags)); + sp<EventThreadConnection>(ResyncCallback, EventRegistrationFlags)); MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); - MOCK_METHOD1(onModeChanged, void(DisplayModePtr)); + MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &)); MOCK_METHOD2(onFrameRateOverridesChanged, void(PhysicalDisplayId, std::vector<FrameRateOverride>)); MOCK_CONST_METHOD1(dump, void(std::string&)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h new file mode 100644 index 0000000000..ef9cd9bc43 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 <scheduler/FrameRateMode.h> + +// Use a C style macro to keep the line numbers printed in gtest +#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \ + EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode)) diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 0840a2f77c..0d94f4c644 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -18,14 +18,15 @@ #include <gmock/gmock.h> -#include "Layer.h" - namespace android::mock { class MockLayer : public Layer { public: MockLayer(SurfaceFlinger* flinger, std::string name) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) { + EXPECT_CALL(*this, getDefaultFrameRateCompatibility()) + .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default)); + } explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} MOCK_CONST_METHOD0(getType, const char*()); @@ -33,6 +34,8 @@ public: MOCK_CONST_METHOD0(isVisible, bool()); MOCK_METHOD0(createClone, sp<Layer>()); MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate()); + MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, + scheduler::LayerInfo::FrameRateCompatibility()); MOCK_CONST_METHOD0(getOwnerUid, uid_t()); MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace()); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 52675869fa..7d4b159e5e 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -24,14 +24,14 @@ namespace android::scheduler::mock { struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, setVsyncEnabled, (bool), (override)); - MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override)); + MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override)); MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); }; struct NoOpSchedulerCallback final : ISchedulerCallback { void setVsyncEnabled(bool) override {} - void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {} + void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp deleted file mode 100644 index 0a0e7b5861..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp +++ /dev/null @@ -1,32 +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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include "mock/MockSurfaceInterceptor.h" - -namespace android::mock { - -// Explicit default instantiation is recommended. -SurfaceInterceptor::SurfaceInterceptor() = default; -SurfaceInterceptor::~SurfaceInterceptor() = default; - -} // namespace android::mock - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h deleted file mode 100644 index b085027397..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h +++ /dev/null @@ -1,50 +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 <gmock/gmock.h> - -#include "SurfaceInterceptor.h" - -namespace android::mock { - -class SurfaceInterceptor : public android::SurfaceInterceptor { -public: - SurfaceInterceptor(); - ~SurfaceInterceptor() override; - - MOCK_METHOD2(enable, - void(const SortedVector<sp<Layer>>&, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&)); - MOCK_METHOD0(disable, void()); - MOCK_METHOD0(isEnabled, bool()); - MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&)); - MOCK_METHOD1(binderDied, void(const wp<IBinder>&)); - MOCK_METHOD7(saveTransaction, - void(const Vector<ComposerState>&, - const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&, - const Vector<DisplayState>&, uint32_t, int, int, uint64_t)); - MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&)); - MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&)); - MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t)); - MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&)); - MOCK_METHOD1(saveDisplayDeletion, void(int32_t)); - MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t)); - MOCK_METHOD1(saveVSyncEvent, void(nsecs_t)); -}; - -} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 0dee800558..86fbadc0f2 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -27,7 +27,7 @@ public: TimeStats(); ~TimeStats() override; - MOCK_METHOD2(onPullAtom, bool(const int, std::string*)); + MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*)); MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&)); MOCK_METHOD0(isEnabled, bool()); MOCK_METHOD0(miniDump, std::string()); diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 5b0c1f38be..6893154259 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -34,6 +34,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h index 07916b60a7..38c422a26d 100644 --- a/services/surfaceflinger/tests/utils/ColorUtils.h +++ b/services/surfaceflinger/tests/utils/ColorUtils.h @@ -33,6 +33,10 @@ struct Color { static const Color WHITE; static const Color BLACK; static const Color TRANSPARENT; + + half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; } + + half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; } }; const Color Color::RED{255, 0, 0, 255}; @@ -81,6 +85,14 @@ public: } color = ret; } + + static half3 toHalf3(const Color& color) { + return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}; + } + + static half4 toHalf4(const Color& color) { + return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f}; + } }; } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index f879430db0..f297da5511 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -15,8 +15,10 @@ */ #pragma once +#include <gui/AidlStatusUtil.h> #include <gui/SyncScreenCaptureListener.h> #include <private/gui/ComposerServiceAIDL.h> +#include <ui/FenceResult.h> #include <ui/Rect.h> #include <utils/String8.h> #include <functional> @@ -24,6 +26,8 @@ namespace android { +using gui::aidl_utils::statusTFromBinderStatus; + namespace { // A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check @@ -36,18 +40,22 @@ public: SurfaceComposerClient::Transaction().apply(true); captureArgs.dataspace = ui::Dataspace::V0_SRGB; - const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); binder::Status status = sf->captureDisplay(captureArgs, captureListener); - - if (status.transactionError() != NO_ERROR) { - return status.transactionError(); + status_t err = statusTFromBinderStatus(status); + if (err != NO_ERROR) { + return err; } captureResults = captureListener->waitForResults(); - return captureResults.result; + return fenceStatus(captureResults.fenceResult); } static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { - captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken()); + const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + // TODO(b/248317436): extend to cover all displays for multi-display devices + const auto display = + ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + captureScreen(sc, display); } static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) { @@ -70,13 +78,14 @@ public: SurfaceComposerClient::Transaction().apply(true); captureArgs.dataspace = ui::Dataspace::V0_SRGB; - const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); binder::Status status = sf->captureLayers(captureArgs, captureListener); - if (status.transactionError() != NO_ERROR) { - return status.transactionError(); + status_t err = statusTFromBinderStatus(status); + if (err != NO_ERROR) { + return err; } captureResults = captureListener->waitForResults(); - return captureResults.result; + return fenceStatus(captureResults.fenceResult); } static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) { diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp index 667dfb92d5..8b4a6be1bf 100644 --- a/services/surfaceflinger/tests/vsync/vsync.cpp +++ b/services/surfaceflinger/tests/vsync/vsync.cpp @@ -18,7 +18,13 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +// This file is included by modules that have host support but android/looper.h is not supported +// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled. +#ifndef __BIONIC__ +#define __REMOVED_IN(x) __attribute__((deprecated)) +#endif #include <android/looper.h> + #include <gui/DisplayEventReceiver.h> #include <utils/Looper.h> @@ -55,8 +61,7 @@ int main(int /*argc*/, char** /*argv*/) { DisplayEventReceiver myDisplayEvent; - - sp<Looper> loop = new Looper(false); + sp<Looper> loop = sp<Looper>::make(false); loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver, &myDisplayEvent); diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 2e22c36f35..c7284ce52a 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -243,6 +243,11 @@ enum { MAX_TIMING_INFOS = 10 }; // syncronous requests to Surface Flinger): enum { MIN_NUM_FRAMES_AGO = 5 }; +bool IsSharedPresentMode(VkPresentModeKHR mode) { + return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || + mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR; +} + struct Swapchain { Swapchain(Surface& surface_, uint32_t num_images_, @@ -254,9 +259,7 @@ struct Swapchain { pre_transform(pre_transform_), frame_timestamps_enabled(false), acquire_next_image_timeout(-1), - shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || - present_mode == - VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) { + shared(IsSharedPresentMode(present_mode)) { ANativeWindow* window = surface.window.get(); native_window_get_refresh_cycle_duration( window, @@ -754,7 +757,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); - bool wide_color_support = false; uint64_t consumer_usage = 0; bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); @@ -765,27 +767,15 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (!surfaceless_enabled) { return VK_ERROR_SURFACE_LOST_KHR; } - // Support for VK_GOOGLE_surfaceless_query. The EGL loader - // unconditionally supports wide color formats, even if they will cause - // a SurfaceFlinger fallback. Based on that, wide_color_support will be - // set to true in this case. - wide_color_support = true; + // Support for VK_GOOGLE_surfaceless_query. // TODO(b/203826952): research proper value; temporarily use the // values seen on Pixel consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; } else { Surface& surface = *SurfaceFromHandle(surface_handle); - int err = native_window_get_wide_color_support(surface.window.get(), - &wide_color_support); - if (err) { - return VK_ERROR_SURFACE_LOST_KHR; - } - ALOGV("wide_color_support is: %d", wide_color_support); - consumer_usage = surface.consumer_usage; } - wide_color_support = wide_color_support && colorspace_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; @@ -807,9 +797,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - } - - if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -839,8 +826,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - } - if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); @@ -859,8 +844,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - } - if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); @@ -1816,6 +1799,173 @@ static VkResult WorstPresentResult(VkResult a, VkResult b) { return a != VK_SUCCESS ? a : b; } +// KHR_incremental_present aspect of QueuePresentKHR +static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) { + std::vector<android_native_rect_t> rects(pRegion->rectangleCount); + for (auto i = 0u; i < pRegion->rectangleCount; i++) { + auto const& rect = pRegion->pRectangles[i]; + if (rect.layer > 0) { + ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead", + rect.layer); + } + + rects[i].left = rect.offset.x; + rects[i].bottom = rect.offset.y; + rects[i].right = rect.offset.x + rect.extent.width; + rects[i].top = rect.offset.y + rect.extent.height; + } + native_window_set_surface_damage(window, rects.data(), rects.size()); +} + +// GOOGLE_display_timing aspect of QueuePresentKHR +static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) { + ANativeWindow *window = swapchain.surface.window.get(); + + // We don't know whether the app will actually use GOOGLE_display_timing + // with a particular swapchain until QueuePresent; enable it on the BQ + // now if needed + if (!swapchain.frame_timestamps_enabled) { + ALOGV("Calling native_window_enable_frame_timestamps(true)"); + native_window_enable_frame_timestamps(window, true); + swapchain.frame_timestamps_enabled = true; + } + + // Record the nativeFrameId so it can be later correlated to + // this present. + uint64_t nativeFrameId = 0; + int err = native_window_get_next_frame_id( + window, &nativeFrameId); + if (err != android::OK) { + ALOGE("Failed to get next native frame ID."); + } + + // Add a new timing record with the user's presentID and + // the nativeFrameId. + swapchain.timing.emplace_back(pTime, nativeFrameId); + if (swapchain.timing.size() > MAX_TIMING_INFOS) { + swapchain.timing.erase( + swapchain.timing.begin(), + swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS); + } + if (pTime->desiredPresentTime) { + ALOGV( + "Calling native_window_set_buffers_timestamp(%" PRId64 ")", + pTime->desiredPresentTime); + native_window_set_buffers_timestamp( + window, + static_cast<int64_t>(pTime->desiredPresentTime)); + } +} + +static VkResult PresentOneSwapchain( + VkQueue queue, + Swapchain& swapchain, + uint32_t imageIndex, + const VkPresentRegionKHR *pRegion, + const VkPresentTimeGOOGLE *pTime, + uint32_t waitSemaphoreCount, + const VkSemaphore *pWaitSemaphores) { + + VkDevice device = GetData(queue).driver_device; + const auto& dispatch = GetData(queue).driver; + + Swapchain::Image& img = swapchain.images[imageIndex]; + VkResult swapchain_result = VK_SUCCESS; + VkResult result; + int err; + + // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the + // wait semaphores, so this doesn't actually work for the multiple swapchain + // case. + int fence = -1; + result = dispatch.QueueSignalReleaseImageANDROID( + queue, waitSemaphoreCount, + pWaitSemaphores, img.image, &fence); + if (result != VK_SUCCESS) { + ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); + swapchain_result = result; + } + if (img.release_fence >= 0) + close(img.release_fence); + img.release_fence = fence < 0 ? -1 : dup(fence); + + if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) { + ANativeWindow* window = swapchain.surface.window.get(); + if (swapchain_result == VK_SUCCESS) { + + if (pRegion) { + SetSwapchainSurfaceDamage(window, pRegion); + } + if (pTime) { + SetSwapchainFrameTimestamp(swapchain, pTime); + } + + err = window->queueBuffer(window, img.buffer.get(), fence); + // queueBuffer always closes fence, even on error + if (err != android::OK) { + ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err); + swapchain_result = WorstPresentResult( + swapchain_result, VK_ERROR_SURFACE_LOST_KHR); + } else { + if (img.dequeue_fence >= 0) { + close(img.dequeue_fence); + img.dequeue_fence = -1; + } + img.dequeued = false; + } + + // If the swapchain is in shared mode, immediately dequeue the + // buffer so it can be presented again without an intervening + // call to AcquireNextImageKHR. We expect to get the same buffer + // back from every call to dequeueBuffer in this mode. + if (swapchain.shared && swapchain_result == VK_SUCCESS) { + ANativeWindowBuffer* buffer; + int fence_fd; + err = window->dequeueBuffer(window, &buffer, &fence_fd); + if (err != android::OK) { + ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); + swapchain_result = WorstPresentResult(swapchain_result, + VK_ERROR_SURFACE_LOST_KHR); + } else if (img.buffer != buffer) { + ALOGE("got wrong image back for shared swapchain"); + swapchain_result = WorstPresentResult(swapchain_result, + VK_ERROR_SURFACE_LOST_KHR); + } else { + img.dequeue_fence = fence_fd; + img.dequeued = true; + } + } + } + if (swapchain_result != VK_SUCCESS) { + OrphanSwapchain(device, &swapchain); + } + // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR, + // and only when the window's transform/rotation changes. Extent + // changes will not cause VK_SUBOPTIMAL_KHR because of the + // application issues that were caused when the following transform + // change was added. + int window_transform_hint; + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, + &window_transform_hint); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + swapchain_result = WorstPresentResult( + swapchain_result, VK_ERROR_SURFACE_LOST_KHR); + } + if (swapchain.pre_transform != window_transform_hint) { + swapchain_result = + WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR); + } + } else { + ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence, + img, true); + swapchain_result = VK_ERROR_OUT_OF_DATE_KHR; + } + + return swapchain_result; +} + VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { ATRACE_CALL(); @@ -1824,8 +1974,6 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d", present_info->sType); - VkDevice device = GetData(queue).driver_device; - const auto& dispatch = GetData(queue).driver; VkResult final_result = VK_SUCCESS; // Look at the pNext chain for supported extension structs: @@ -1861,170 +2009,19 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = (present_times) ? present_times->pTimes : nullptr; - const VkAllocationCallbacks* allocator = &GetData(device).allocator; - android_native_rect_t* rects = nullptr; - uint32_t nrects = 0; for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) { Swapchain& swapchain = *SwapchainFromHandle(present_info->pSwapchains[sc]); - uint32_t image_idx = present_info->pImageIndices[sc]; - Swapchain::Image& img = swapchain.images[image_idx]; - const VkPresentRegionKHR* region = - (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr; - const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr; - VkResult swapchain_result = VK_SUCCESS; - VkResult result; - int err; - - int fence = -1; - result = dispatch.QueueSignalReleaseImageANDROID( - queue, present_info->waitSemaphoreCount, - present_info->pWaitSemaphores, img.image, &fence); - if (result != VK_SUCCESS) { - ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); - swapchain_result = result; - } - if (img.release_fence >= 0) - close(img.release_fence); - img.release_fence = fence < 0 ? -1 : dup(fence); - - if (swapchain.surface.swapchain_handle == - present_info->pSwapchains[sc]) { - ANativeWindow* window = swapchain.surface.window.get(); - if (swapchain_result == VK_SUCCESS) { - if (region) { - // Process the incremental-present hint for this swapchain: - uint32_t rcount = region->rectangleCount; - if (rcount > nrects) { - android_native_rect_t* new_rects = - static_cast<android_native_rect_t*>( - allocator->pfnReallocation( - allocator->pUserData, rects, - sizeof(android_native_rect_t) * rcount, - alignof(android_native_rect_t), - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND)); - if (new_rects) { - rects = new_rects; - nrects = rcount; - } else { - rcount = 0; // Ignore the hint for this swapchain - } - } - for (uint32_t r = 0; r < rcount; ++r) { - if (region->pRectangles[r].layer > 0) { - ALOGV( - "vkQueuePresentKHR ignoring invalid layer " - "(%u); using layer 0 instead", - region->pRectangles[r].layer); - } - int x = region->pRectangles[r].offset.x; - int y = region->pRectangles[r].offset.y; - int width = static_cast<int>( - region->pRectangles[r].extent.width); - int height = static_cast<int>( - region->pRectangles[r].extent.height); - android_native_rect_t* cur_rect = &rects[r]; - cur_rect->left = x; - cur_rect->top = y + height; - cur_rect->right = x + width; - cur_rect->bottom = y; - } - native_window_set_surface_damage(window, rects, rcount); - } - if (time) { - if (!swapchain.frame_timestamps_enabled) { - ALOGV( - "Calling " - "native_window_enable_frame_timestamps(true)"); - native_window_enable_frame_timestamps(window, true); - swapchain.frame_timestamps_enabled = true; - } - - // Record the nativeFrameId so it can be later correlated to - // this present. - uint64_t nativeFrameId = 0; - err = native_window_get_next_frame_id( - window, &nativeFrameId); - if (err != android::OK) { - ALOGE("Failed to get next native frame ID."); - } - // Add a new timing record with the user's presentID and - // the nativeFrameId. - swapchain.timing.emplace_back(time, nativeFrameId); - while (swapchain.timing.size() > MAX_TIMING_INFOS) { - swapchain.timing.erase(swapchain.timing.begin()); - } - if (time->desiredPresentTime) { - // Set the desiredPresentTime: - ALOGV( - "Calling " - "native_window_set_buffers_timestamp(%" PRId64 ")", - time->desiredPresentTime); - native_window_set_buffers_timestamp( - window, - static_cast<int64_t>(time->desiredPresentTime)); - } - } - - err = window->queueBuffer(window, img.buffer.get(), fence); - // queueBuffer always closes fence, even on error - if (err != android::OK) { - ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err); - swapchain_result = WorstPresentResult( - swapchain_result, VK_ERROR_SURFACE_LOST_KHR); - } else { - if (img.dequeue_fence >= 0) { - close(img.dequeue_fence); - img.dequeue_fence = -1; - } - img.dequeued = false; - } - - // If the swapchain is in shared mode, immediately dequeue the - // buffer so it can be presented again without an intervening - // call to AcquireNextImageKHR. We expect to get the same buffer - // back from every call to dequeueBuffer in this mode. - if (swapchain.shared && swapchain_result == VK_SUCCESS) { - ANativeWindowBuffer* buffer; - int fence_fd; - err = window->dequeueBuffer(window, &buffer, &fence_fd); - if (err != android::OK) { - ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); - swapchain_result = WorstPresentResult(swapchain_result, - VK_ERROR_SURFACE_LOST_KHR); - } else if (img.buffer != buffer) { - ALOGE("got wrong image back for shared swapchain"); - swapchain_result = WorstPresentResult(swapchain_result, - VK_ERROR_SURFACE_LOST_KHR); - } else { - img.dequeue_fence = fence_fd; - img.dequeued = true; - } - } - } - if (swapchain_result != VK_SUCCESS) { - OrphanSwapchain(device, &swapchain); - } - int window_transform_hint; - err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, - &window_transform_hint); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", - strerror(-err), err); - swapchain_result = WorstPresentResult( - swapchain_result, VK_ERROR_SURFACE_LOST_KHR); - } - if (swapchain.pre_transform != window_transform_hint) { - swapchain_result = - WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR); - } - } else { - ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence, - img, true); - swapchain_result = VK_ERROR_OUT_OF_DATE_KHR; - } + VkResult swapchain_result = PresentOneSwapchain( + queue, + swapchain, + present_info->pImageIndices[sc], + (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr, + times ? ×[sc] : nullptr, + present_info->waitSemaphoreCount, + present_info->pWaitSemaphores); if (present_info->pResults) present_info->pResults[sc] = swapchain_result; @@ -2032,9 +2029,6 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { if (swapchain_result != final_result) final_result = WorstPresentResult(final_result, swapchain_result); } - if (rects) { - allocator->pfnFree(allocator->pUserData, rects); - } return final_result; } |